import React, { useState, useEffect } from 'react' import { useParams, useNavigate, Link } from 'react-router-dom' import { Editor } from '@monaco-editor/react' import { FaPlay, FaCopy, FaCheckCircle, FaExclamationCircle, FaSpinner, FaExternalLinkAlt, FaArrowLeft, FaCog, FaCode, FaSave, } from 'react-icons/fa' import { useLocalization } from '@/utils/hooks/useLocalization' import { dynamicServiceService, type CompileResult, type PublishResult, postTestCompile, type TestCompileDto, } from '@/services/dynamicService.service' import { Helmet } from 'react-helmet' import { APP_NAME } from '@/constants/app.constant' import { ROUTES_ENUM } from '@/routes/route.constant' 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 { public virtual async Task GetHelloWorldAsync() { return await Task.FromResult("Hello World from Dynamic AppService!"); } public virtual async Task> GetSampleDataAsync() { return await Task.FromResult(new List { "Item 1", "Item 2", "Item 3" }); } } }` const DynamicServiceEditor: React.FC = () => { const { id } = useParams<{ id: string }>() const navigate = useNavigate() const { translate } = useLocalization() const [code, setCode] = useState(defaultTemplate) const [serviceName, setServiceName] = useState('') const [displayName, setDisplayName] = useState('') const [description, setDescription] = useState('') const [primaryEntityType, setPrimaryEntityType] = useState('') const [isActive, setIsActive] = useState(true) const [submitted, setSubmitted] = useState(false) const [isCompiling, setIsCompiling] = useState(false) const [isPublishing, setIsPublishing] = useState(false) const [isLoading, setIsLoading] = useState(false) const [compileResult, setCompileResult] = useState(null) const [publishResult, setPublishResult] = useState(null) const editorOptions = { fontSize: 14, lineNumbers: 'on' as const, roundedSelection: false, scrollBeyondLastLine: false, automaticLayout: true, minimap: { enabled: false }, folding: true, wordWrap: 'on' as const, } useEffect(() => { if (id) { loadService(id) } }, [id]) const loadService = async (serviceId: string) => { try { setIsLoading(true) const data = await dynamicServiceService.getById(serviceId) setCode(data.code) setServiceName(data.name) setDisplayName(data.displayName || '') setDescription(data.description || '') setPrimaryEntityType(data.primaryEntityType || '') setIsActive(data.isActive ?? true) } catch (error) { console.error('Servis yüklenirken hata:', error) } finally { setIsLoading(false) } } const handleTestCompile = async () => { if (!code.trim()) { alert('Lütfen kod girin') return } try { setIsCompiling(true) setCompileResult(null) const result = await postTestCompile({ code } as TestCompileDto) setCompileResult(result.data) } catch (error: any) { setCompileResult({ success: false, errorMessage: error.response?.data?.message || 'Derleme sırasında hata oluştu', compilationTimeMs: 0, hasWarnings: false, errors: [], }) } finally { setIsCompiling(false) } } const handlePublish = async () => { setSubmitted(true) if (!code.trim() || !serviceName.trim()) { return } if (isPublishing) return try { setIsPublishing(true) setPublishResult(null) // Edit modunda: önce eskiyi sil, sonra yeniden yayınla if (id) { await dynamicServiceService.delete(id) } const result = await dynamicServiceService.publish({ name: serviceName, code, displayName, description, primaryEntityType, isActive, }) if (result.success) { navigate(ROUTES_ENUM.protected.saas.developerKit.dynamicServices) } else { setPublishResult(result) } } catch (error: any) { setPublishResult({ success: false, errorMessage: error.response?.data?.message || 'Yayınlama sırasında hata oluştu', }) } finally { setIsPublishing(false) } } const copyCode = () => { navigator.clipboard.writeText(code) alert('Kod panoya kopyalandı') } const pageTitle = id ? `Servis Düzenle` : `Yeni Servis` if (isLoading) { return (
) } const serviceNameError = submitted && !serviceName.trim() return (
{/* Header */}
{/* Left: back + icon + title */}
Servislere Dön

{pageTitle}

{id ? 'Mevcut servisi düzenleyin' : 'Yeni bir dynamic servis oluşturun'}

{/* Right: action buttons + swagger + publish */}
{/* Compile / Publish result banners */} {compileResult && (
{compileResult.success ? ( ) : ( )}
Derleme {compileResult.success ? 'Başarılı' : 'Başarısız'} {!compileResult.success && compileResult.errors && compileResult.errors.length > 0 && (
    {compileResult.errors.map((e, i) => (
  • [{e.code}] Satır {e.line}: {e.message}
  • ))}
)}
{compileResult.compilationTimeMs}ms
)} {publishResult && !publishResult.success && (
Yayınlama Başarısız {publishResult.errorMessage && (

{publishResult.errorMessage}

)}
)} {/* Two-panel layout */}
{/* LEFT PANEL — Servis Ayarları */}
{/* Panel header */}

Servis Ayarları

{/* Servis Adı */}
{ setServiceName(e.target.value) setSubmitted(false) }} placeholder="ör: DynamicCustomerAppService" className={`w-full px-3 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors ${ serviceNameError ? 'border-red-500 bg-red-50' : 'border-slate-300' }`} /> {serviceNameError &&

Servis adı zorunludur

}
{/* Görünen Ad */}
setDisplayName(e.target.value)} placeholder="ör: Müşteri Yönetimi" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Açıklama */}
setDescription(e.target.value)} placeholder="Bu servisin kısa açıklaması" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Ana Entity Türü */}
setPrimaryEntityType(e.target.value)} placeholder="ör: Customer" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Aktif */}
setIsActive(e.target.checked)} className="w-4 h-4 rounded accent-blue-600 cursor-pointer" />
{/* RIGHT PANEL — Önizleme + Editor */}
{/* Monaco Editor */}

C# Kod Editörü

Satır: {code.split('\n').length} | Karakter: {code.length}
setCode(value || '')} options={editorOptions} theme="vs-dark" />
) } export default DynamicServiceEditor