2026-02-24 20:44:16 +00:00
|
|
|
|
import React, { useState, useRef, useEffect } from 'react'
|
|
|
|
|
|
import { Editor } from '@monaco-editor/react'
|
|
|
|
|
|
import {
|
|
|
|
|
|
FaPlay,
|
|
|
|
|
|
FaUpload,
|
|
|
|
|
|
FaCode,
|
|
|
|
|
|
FaCheckCircle,
|
|
|
|
|
|
FaExclamationCircle,
|
|
|
|
|
|
FaSpinner,
|
|
|
|
|
|
FaCopy,
|
|
|
|
|
|
FaExternalLinkAlt,
|
|
|
|
|
|
FaTrash,
|
|
|
|
|
|
FaSync,
|
|
|
|
|
|
} from 'react-icons/fa'
|
|
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import {
|
|
|
|
|
|
dynamicServiceService,
|
|
|
|
|
|
type CompileResult,
|
|
|
|
|
|
type PublishResult,
|
|
|
|
|
|
type DynamicServiceDto,
|
|
|
|
|
|
postTestCompile,
|
|
|
|
|
|
TestCompileDto,
|
|
|
|
|
|
} from '@/services/dynamicService.service'
|
2026-03-01 20:43:25 +00:00
|
|
|
|
import { Helmet } from 'react-helmet'
|
|
|
|
|
|
import { APP_NAME } from '@/constants/app.constant'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
const DynamicAppServiceEditor: React.FC = () => {
|
|
|
|
|
|
// State
|
|
|
|
|
|
const [code, setCode] = useState('')
|
|
|
|
|
|
const [serviceName, setServiceName] = useState('')
|
|
|
|
|
|
const [displayName, setDisplayName] = useState('')
|
|
|
|
|
|
const [description, setDescription] = useState('')
|
|
|
|
|
|
const [primaryEntityType, setPrimaryEntityType] = useState('')
|
|
|
|
|
|
|
|
|
|
|
|
const [isCompiling, setIsCompiling] = useState(false)
|
|
|
|
|
|
const [isPublishing, setIsPublishing] = useState(false)
|
|
|
|
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
const [compileResult, setCompileResult] = useState<CompileResult | null>(null)
|
|
|
|
|
|
const [publishResult, setPublishResult] = useState<PublishResult | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const [services, setServices] = useState<DynamicServiceDto[]>([])
|
|
|
|
|
|
const [selectedService, setSelectedService] = useState<DynamicServiceDto | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const [showServiceList, setShowServiceList] = useState(true)
|
|
|
|
|
|
|
|
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
|
|
|
|
|
|
// Template kod
|
|
|
|
|
|
const defaultTemplate = `using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Volo.Abp.Application.Services;
|
|
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
|
|
|
|
|
|
namespace DynamicServices
|
|
|
|
|
|
{
|
|
|
|
|
|
[Authorize]
|
|
|
|
|
|
public class DynamicCustomerAppService : ApplicationService
|
|
|
|
|
|
{
|
|
|
|
|
|
// Repository injection örneği (kendi entity'nizi kullanın)
|
|
|
|
|
|
// private readonly IRepository<Customer, Guid> _customerRepository;
|
|
|
|
|
|
|
|
|
|
|
|
// public DynamicCustomerAppService(IRepository<Customer, Guid> customerRepository)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// _customerRepository = customerRepository;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
public virtual async Task<string> GetHelloWorldAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
return await Task.FromResult("Hello World from Dynamic AppService!");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual async Task<List<string>> GetSampleDataAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
return await Task.FromResult(new List<string>
|
|
|
|
|
|
{
|
|
|
|
|
|
"Item 1",
|
|
|
|
|
|
"Item 2",
|
|
|
|
|
|
"Item 3"
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Repository kullanım örneği:
|
|
|
|
|
|
// public virtual async Task<List<Customer>> GetCustomersAsync()
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return await _customerRepository.GetListAsync();
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
}`
|
|
|
|
|
|
|
|
|
|
|
|
// Component mount
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
setCode(defaultTemplate)
|
|
|
|
|
|
loadServices()
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
// Monaco Editor ayarları
|
|
|
|
|
|
const editorOptions = {
|
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
|
lineNumbers: 'on' as const,
|
|
|
|
|
|
roundedSelection: false,
|
|
|
|
|
|
scrollBeyondLastLine: false,
|
|
|
|
|
|
automaticLayout: true,
|
|
|
|
|
|
minimap: { enabled: false },
|
|
|
|
|
|
folding: true,
|
|
|
|
|
|
wordWrap: 'on' as const,
|
|
|
|
|
|
theme: 'vs-dark',
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Servisleri yükle
|
|
|
|
|
|
const loadServices = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setIsLoading(true)
|
|
|
|
|
|
const response = await dynamicServiceService.getList()
|
|
|
|
|
|
setServices(response.items || [])
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Servisler yüklenirken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test compile
|
|
|
|
|
|
const handleTestCompile = async () => {
|
|
|
|
|
|
if (!code.trim()) {
|
|
|
|
|
|
alert('Lütfen kod girin')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setIsCompiling(true)
|
|
|
|
|
|
setCompileResult(null)
|
|
|
|
|
|
console.log('Test compile code:', code)
|
|
|
|
|
|
const input = { code: code } as TestCompileDto
|
|
|
|
|
|
const result = await postTestCompile(input)
|
|
|
|
|
|
setCompileResult(result.data)
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('Test compile error:', error)
|
|
|
|
|
|
console.error('Error response:', error.response?.data)
|
|
|
|
|
|
setCompileResult({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
errorMessage: error.response?.data?.message || 'Derleme sırasında hata oluştu',
|
|
|
|
|
|
compilationTimeMs: 0,
|
|
|
|
|
|
hasWarnings: false,
|
|
|
|
|
|
errors: [],
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsCompiling(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Publish
|
|
|
|
|
|
const handlePublish = async () => {
|
|
|
|
|
|
if (!code.trim() || !serviceName.trim()) {
|
|
|
|
|
|
alert('Lütfen kod ve servis adını girin')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
setIsPublishing(true)
|
|
|
|
|
|
setPublishResult(null)
|
|
|
|
|
|
|
|
|
|
|
|
const requestData = {
|
|
|
|
|
|
name: serviceName,
|
|
|
|
|
|
code: code,
|
|
|
|
|
|
displayName: displayName,
|
|
|
|
|
|
description: description,
|
|
|
|
|
|
primaryEntityType: primaryEntityType,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = await dynamicServiceService.publish(requestData)
|
|
|
|
|
|
setPublishResult(result)
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
await loadServices() // Listeyi yenile
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
console.error('Publish error:', error)
|
|
|
|
|
|
console.error('Error response:', error.response?.data)
|
|
|
|
|
|
setPublishResult({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
errorMessage: error.response?.data?.message || 'Yayınlama sırasında hata oluştu',
|
|
|
|
|
|
})
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsPublishing(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Servisi yükle
|
|
|
|
|
|
const loadService = async (service: DynamicServiceDto) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await dynamicServiceService.getById(service.id)
|
|
|
|
|
|
|
|
|
|
|
|
setSelectedService(data)
|
|
|
|
|
|
setCode(data.code)
|
|
|
|
|
|
setServiceName(data.name)
|
|
|
|
|
|
setDisplayName(data.displayName || '')
|
|
|
|
|
|
setDescription(data.description || '')
|
|
|
|
|
|
setPrimaryEntityType(data.primaryEntityType || '')
|
|
|
|
|
|
|
|
|
|
|
|
setCompileResult(null)
|
|
|
|
|
|
setPublishResult(null)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Servis yüklenirken hata:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Servisi sil
|
|
|
|
|
|
const deleteService = async (serviceId: string) => {
|
|
|
|
|
|
if (!confirm('Bu servisi silmek istediğinizden emin misiniz?')) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await dynamicServiceService.delete(serviceId)
|
|
|
|
|
|
await loadServices()
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedService?.id === serviceId) {
|
|
|
|
|
|
setSelectedService(null)
|
|
|
|
|
|
setCode(defaultTemplate)
|
|
|
|
|
|
setServiceName('')
|
|
|
|
|
|
setDisplayName('')
|
|
|
|
|
|
setDescription('')
|
|
|
|
|
|
setPrimaryEntityType('')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Servis silinirken hata:', error)
|
|
|
|
|
|
alert('Servis silinirken hata oluştu')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Yeni servis
|
|
|
|
|
|
const newService = () => {
|
|
|
|
|
|
setSelectedService(null)
|
|
|
|
|
|
setCode(defaultTemplate)
|
|
|
|
|
|
setServiceName('')
|
|
|
|
|
|
setDisplayName('')
|
|
|
|
|
|
setDescription('')
|
|
|
|
|
|
setPrimaryEntityType('')
|
|
|
|
|
|
setCompileResult(null)
|
|
|
|
|
|
setPublishResult(null)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Swagger aç
|
|
|
|
|
|
const openSwagger = () => {
|
|
|
|
|
|
window.open(`${import.meta.env.VITE_API_URL}/swagger/index.html`, '_blank')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Kodu kopyala
|
|
|
|
|
|
const copyCode = () => {
|
|
|
|
|
|
navigator.clipboard.writeText(code)
|
|
|
|
|
|
alert('Kod panoya kopyalandı')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="space-y-4">
|
2026-03-01 20:43:25 +00:00
|
|
|
|
<Helmet
|
|
|
|
|
|
titleTemplate={`%s | ${APP_NAME}`}
|
|
|
|
|
|
title={translate('::' + 'App.DeveloperKit.DynamicServices')}
|
|
|
|
|
|
defaultTitle={APP_NAME}
|
|
|
|
|
|
></Helmet>
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<div className="flex items-center justify-between mb-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h1 className="text-2xl font-bold text-slate-900">
|
|
|
|
|
|
{translate('::App.DeveloperKit.DynamicServices')}
|
|
|
|
|
|
</h1>
|
|
|
|
|
|
<p className="text-slate-600">
|
|
|
|
|
|
{translate('::App.DeveloperKit.DynamicServices.Description')}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={openSwagger}
|
|
|
|
|
|
className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaExternalLinkAlt className="w-4 h-4" />
|
|
|
|
|
|
Swagger
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => setShowServiceList(!showServiceList)}
|
|
|
|
|
|
className="flex items-center gap-2 bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaCode className="w-4 h-4" />
|
|
|
|
|
|
{showServiceList ? 'Listeyi Gizle' : 'Listeyi Göster'}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-12 gap-6">
|
|
|
|
|
|
{/* Service List */}
|
|
|
|
|
|
{showServiceList && (
|
|
|
|
|
|
<div className="col-span-12 lg:col-span-4">
|
|
|
|
|
|
<div className="bg-white rounded-lg shadow-sm border">
|
|
|
|
|
|
<div className="p-4 border-b">
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<h3 className="text-lg font-semibold">Mevcut Servisler</h3>
|
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={newService}
|
|
|
|
|
|
className="bg-green-600 text-white px-3 py-1 rounded text-sm hover:bg-green-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaCode className="w-4 h-4 inline mr-1" />
|
|
|
|
|
|
Yeni
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={loadServices}
|
|
|
|
|
|
className="bg-gray-600 text-white px-3 py-1 rounded text-sm hover:bg-gray-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaSync className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="max-h-96 overflow-y-auto">
|
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
|
<div className="p-4 text-center">
|
|
|
|
|
|
<FaSpinner className="w-6 h-6 animate-spin mx-auto mb-2" />
|
|
|
|
|
|
Yükleniyor...
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : services.length > 0 ? (
|
|
|
|
|
|
services.map((service) => (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={service.id}
|
|
|
|
|
|
className={`p-3 border-b hover:bg-gray-50 cursor-pointer ${
|
|
|
|
|
|
selectedService?.id === service.id
|
|
|
|
|
|
? 'bg-blue-50 border-l-4 border-l-blue-500'
|
|
|
|
|
|
: ''
|
|
|
|
|
|
}`}
|
|
|
|
|
|
onClick={() => loadService(service)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
|
<h4 className="font-medium text-gray-900">{service.name}</h4>
|
|
|
|
|
|
{service.displayName && (
|
|
|
|
|
|
<p className="text-sm text-gray-600">{service.displayName}</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
<div className="flex items-center gap-2 mt-1">
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={`px-2 py-1 text-xs rounded ${
|
|
|
|
|
|
service.compilationStatus === 'Success'
|
|
|
|
|
|
? 'bg-green-100 text-green-800'
|
|
|
|
|
|
: service.compilationStatus === 'Failed'
|
|
|
|
|
|
? 'bg-red-100 text-red-800'
|
|
|
|
|
|
: 'bg-yellow-100 text-yellow-800'
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{service.compilationStatus}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-xs text-gray-500">v{service.version}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
|
deleteService(service.id)
|
|
|
|
|
|
}}
|
|
|
|
|
|
className="text-red-600 hover:text-red-800 p-1"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaTrash className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="p-4 text-center text-gray-500">Henüz servis yok</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Main Editor */}
|
|
|
|
|
|
<div className={`col-span-12 ${showServiceList ? 'lg:col-span-8' : ''}`}>
|
|
|
|
|
|
{/* Service Info Form */}
|
|
|
|
|
|
<div className="bg-white rounded-lg shadow-sm border p-4 mb-4">
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Servis Adı *</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={serviceName}
|
|
|
|
|
|
onChange={(e) => setServiceName(e.target.value)}
|
|
|
|
|
|
placeholder="ör: DynamicCustomerAppService"
|
|
|
|
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Görünen Ad</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={displayName}
|
|
|
|
|
|
onChange={(e) => setDisplayName(e.target.value)}
|
|
|
|
|
|
placeholder="ör: Müşteri Yönetimi"
|
|
|
|
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="md:col-span-2">
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Açıklama</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={description}
|
|
|
|
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
|
|
|
|
placeholder="Bu servisin ne yaptığını açıklayın..."
|
|
|
|
|
|
rows={2}
|
|
|
|
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Ana Entity Türü
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={primaryEntityType}
|
|
|
|
|
|
onChange={(e) => setPrimaryEntityType(e.target.value)}
|
|
|
|
|
|
placeholder="ör: Customer"
|
|
|
|
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Action Buttons */}
|
|
|
|
|
|
<div className="bg-white rounded-lg shadow-sm border p-4 mb-4">
|
|
|
|
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleTestCompile}
|
|
|
|
|
|
disabled={isCompiling || !code.trim()}
|
|
|
|
|
|
className="flex items-center gap-2 bg-orange-600 text-white px-4 py-2 rounded-lg hover:bg-orange-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
{isCompiling ? (
|
|
|
|
|
|
<FaSpinner className="w-4 h-4 animate-spin" />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<FaPlay className="w-4 h-4" />
|
|
|
|
|
|
)}
|
|
|
|
|
|
Test Compile
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handlePublish}
|
|
|
|
|
|
disabled={isPublishing || !code.trim() || !serviceName.trim()}
|
|
|
|
|
|
className="flex items-center gap-2 bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
{isPublishing ? (
|
|
|
|
|
|
<FaSpinner className="w-4 h-4 animate-spin" />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<FaUpload className="w-4 h-4" />
|
|
|
|
|
|
)}
|
|
|
|
|
|
Publish
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={copyCode}
|
|
|
|
|
|
className="flex items-center gap-2 bg-gray-600 text-white px-4 py-2 rounded-lg hover:bg-gray-700 transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaCopy className="w-4 h-4" />
|
|
|
|
|
|
Kopyala
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Results */}
|
|
|
|
|
|
{(compileResult || publishResult) && (
|
|
|
|
|
|
<div className="space-y-4 mb-4">
|
|
|
|
|
|
{/* Compile Result */}
|
|
|
|
|
|
{compileResult && (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className={`rounded-lg border p-4 ${
|
|
|
|
|
|
compileResult.success
|
|
|
|
|
|
? 'bg-green-50 border-green-200'
|
|
|
|
|
|
: 'bg-red-50 border-red-200'
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex items-center gap-2 mb-2">
|
|
|
|
|
|
{compileResult.success ? (
|
|
|
|
|
|
<FaCheckCircle className="w-5 h-5 text-green-600" />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<FaExclamationCircle className="w-5 h-5 text-red-600" />
|
|
|
|
|
|
)}
|
|
|
|
|
|
<h4
|
|
|
|
|
|
className={`font-medium ${
|
|
|
|
|
|
compileResult.success ? 'text-green-800' : 'text-red-800'
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
Derleme {compileResult.success ? 'Başarılı' : 'Başarısız'}
|
|
|
|
|
|
</h4>
|
|
|
|
|
|
<span className="text-sm text-gray-600">
|
|
|
|
|
|
({compileResult.compilationTimeMs}ms)
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{!compileResult.success &&
|
|
|
|
|
|
compileResult.errors &&
|
|
|
|
|
|
compileResult.errors.length > 0 && (
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{compileResult.errors.map((error, index) => (
|
|
|
|
|
|
<div key={index} className="bg-white rounded p-3 border">
|
|
|
|
|
|
<div className="flex items-start gap-2">
|
|
|
|
|
|
<span className="text-red-600 font-mono text-sm">{error.code}</span>
|
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
|
<p className="text-sm text-gray-800">{error.message}</p>
|
|
|
|
|
|
<p className="text-xs text-gray-600 mt-1">
|
|
|
|
|
|
Satır {error.line}, Sütun {error.column}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{compileResult.hasWarnings && compileResult.warnings && (
|
|
|
|
|
|
<div className="mt-3">
|
|
|
|
|
|
<h5 className="text-sm font-medium text-yellow-800 mb-2">Uyarılar:</h5>
|
|
|
|
|
|
<ul className="list-disc list-inside space-y-1">
|
|
|
|
|
|
{compileResult.warnings.map((warning, index) => (
|
|
|
|
|
|
<li key={index} className="text-sm text-yellow-700">
|
|
|
|
|
|
{warning}
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Publish Result */}
|
|
|
|
|
|
{publishResult && (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className={`rounded-lg border p-4 ${
|
|
|
|
|
|
publishResult.success
|
|
|
|
|
|
? 'bg-green-50 border-green-200'
|
|
|
|
|
|
: 'bg-red-50 border-red-200'
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex items-center gap-2 mb-2">
|
|
|
|
|
|
{publishResult.success ? (
|
|
|
|
|
|
<FaCheckCircle className="w-5 h-5 text-green-600" />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<FaExclamationCircle className="w-5 h-5 text-red-600" />
|
|
|
|
|
|
)}
|
|
|
|
|
|
<h4
|
|
|
|
|
|
className={`font-medium ${
|
|
|
|
|
|
publishResult.success ? 'text-green-800' : 'text-red-800'
|
|
|
|
|
|
}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
Yayınlama {publishResult.success ? 'Başarılı' : 'Başarısız'}
|
|
|
|
|
|
</h4>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{publishResult.success && (
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{publishResult.controllerName && (
|
|
|
|
|
|
<p className="text-sm text-gray-600">
|
|
|
|
|
|
Controller:{' '}
|
|
|
|
|
|
<code className="bg-gray-100 px-2 py-1 rounded">
|
|
|
|
|
|
{publishResult.controllerName}
|
|
|
|
|
|
</code>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{publishResult.generatedEndpoints &&
|
|
|
|
|
|
publishResult.generatedEndpoints.length > 0 && (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h5 className="text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Oluşturulan Endpoint'ler:
|
|
|
|
|
|
</h5>
|
|
|
|
|
|
<ul className="list-disc list-inside space-y-1">
|
|
|
|
|
|
{publishResult.generatedEndpoints.map((endpoint, index) => (
|
|
|
|
|
|
<li key={index} className="text-sm text-gray-600 font-mono">
|
|
|
|
|
|
{endpoint}
|
|
|
|
|
|
</li>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{!publishResult.success && publishResult.errorMessage && (
|
|
|
|
|
|
<p className="text-red-700 text-sm">{publishResult.errorMessage}</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Monaco Editor */}
|
|
|
|
|
|
<div className="bg-white rounded-lg shadow-sm border overflow-hidden">
|
|
|
|
|
|
<div className="p-3 bg-gray-50 border-b flex items-center justify-between">
|
|
|
|
|
|
<h3 className="font-medium text-gray-700">C# Code Editor</h3>
|
|
|
|
|
|
<div className="flex items-center gap-2 text-sm text-gray-600">
|
|
|
|
|
|
<span>Lines: {code.split('\n').length}</span>
|
|
|
|
|
|
<span>|</span>
|
|
|
|
|
|
<span>Characters: {code.length}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ height: '600px' }}>
|
|
|
|
|
|
<Editor
|
|
|
|
|
|
defaultLanguage="csharp"
|
|
|
|
|
|
value={code}
|
|
|
|
|
|
onChange={(value) => setCode(value || '')}
|
|
|
|
|
|
options={editorOptions}
|
|
|
|
|
|
theme="vs-dark"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default DynamicAppServiceEditor
|