SQL Query Managerda RunSql Script

This commit is contained in:
Sedat Öztürk 2026-03-18 21:53:51 +03:00
parent ff58614f0d
commit 63695be34e
5 changed files with 335 additions and 107 deletions

View file

@ -10428,6 +10428,30 @@
"tr": "Seçili Nesneler",
"en": "Selected Objects"
},
{
"resourceName": "Platform",
"key": "App.Platform.CopyObjects",
"tr": "Nesneleri Kopyala",
"en": "Copy Objects"
},
{
"resourceName": "Platform",
"key": "App.Platform.DirectSqlScript",
"tr": "Doğrudan SQL Scripti",
"en": "Direct SQL Script"
},
{
"resourceName": "Platform",
"key": "App.Platform.SqlScriptPlaceholder",
"tr": "SQL scriptinizi buraya girin",
"en": "Enter your SQL script here"
},
{
"resourceName": "Platform",
"key": "App.Platform.NoObjectSelected",
"tr": "Nesne Seçilmedi",
"en": "No Object Selected"
},
{
"resourceName": "Platform",
"key": "App.Platform.SourceDataSource",
@ -10650,12 +10674,6 @@
"tr": "Fonksiyon",
"en": "Function"
},
{
"resourceName": "Platform",
"key": "App.Platform.Object",
"tr": "Nesne",
"en": "Object"
},
{
"resourceName": "Platform",
"key": "App.Platform.Draft",
@ -11600,7 +11618,7 @@
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.AnnouncementDetailModal.Close",
"key": "App.Platform.Close",
"tr": "Kapat",
"en": "Close"
},
@ -17051,6 +17069,24 @@
"en": "Overwrite if exists",
"tr": "Varsa üzerine yaz"
},
{
"resourceName": "Platform",
"key": "App.Platform.ExecutionCompleted",
"en": "Execution completed",
"tr": "Yürütme tamamlandı"
},
{
"resourceName": "Platform",
"key": "App.Platform.CopyCompleted",
"en": "Copy completed",
"tr": "Kopyalama tamamlandı"
},
{
"resourceName": "Platform",
"key": "App.Platform.CreateScriptFailed",
"en": "Failed to create script",
"tr": "Kaynak objenin scripti oluşturulamadı"
},
{
"resourceName": "Platform",
"key": "App.Platform.OverwriteIfExistsDesc",
@ -17136,12 +17172,6 @@
"en": "Table successfully created",
"tr": "Tablo başarıyla oluşturuldu"
},
{
"resourceName": "Platform",
"key": "App.SqlQueryManager.Error",
"en": "Error",
"tr": "Hata"
},
{
"resourceName": "Platform",
"key": "App.SqlQueryManager.TableCreationFailed",

View file

@ -4142,7 +4142,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
CaptionName = "App.Listform.ListformField.PermissionJson",
Width = 700,
ListOrderNo = 16,
Visible = true,
Visible = false,
IsActive = true,
IsDeleted = false,
ColumnCustomizationJson = DefaultColumnCustomizationJson,

View file

@ -21,7 +21,7 @@ public class ListFormField : FullAuditedEntity<Guid>
public int? SortIndex { get; set; } //bu sütuna göre sıralama yapılacaktır. Birden fazla sütuna göre sıralama varsa, sortindexe göre sıralama yapılır.
public string SortDirection { get; set; } // Sortindex varsa alacagi degerler asc, desc
public bool? AllowSearch { get; set; }
public bool? AllowSearch { get; set; } = true;
public bool? AllowEditing { get; set; }
public string BandName { get; set; }

View file

@ -324,6 +324,7 @@ const Wizard = () => {
const col = selectCommandColumns.find((c) => c.columnName === item.dataField)
return {
dataField: item.dataField,
captionName: `App.Listform.ListformField.${item.dataField}`,
editorType: item.editorType,
editorOptions: item.editorOptions ?? '',
editorScript: item.editorScript ?? '',
@ -340,11 +341,12 @@ const Wizard = () => {
</Notification>,
{ placement: 'top-end' },
)
setTimeout(async () => {
getConfig(true)
setTimeout(() => {
navigate(ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode))
}, 1500)
}, 6000)
}
return (
@ -383,13 +385,12 @@ const Wizard = () => {
{ placement: 'top-end' },
)
setSubmitting(false)
setTimeout(async () => {
getConfig(true)
setTimeout(() => {
navigate(
ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode),
)
}, 1500)
navigate(ROUTES_ENUM.protected.admin.list.replace(':listFormCode', values.listFormCode))
}, 6000)
} catch (error: any) {
toast.push(<Notification title={error.message} type="danger" />, {
placement: 'top-end',

View file

@ -72,6 +72,8 @@ const SqlQueryManager = () => {
const [isCopyingObjects, setIsCopyingObjects] = useState(false)
const [copyResults, setCopyResults] = useState<SqlCopyResultItem[]>([])
const [showCopyResultDialog, setShowCopyResultDialog] = useState(false)
const [copyDialogMode, setCopyDialogMode] = useState<'objects' | 'sql'>('objects')
const [sqlScriptForCopy, setSqlScriptForCopy] = useState('')
useEffect(() => {
loadDataSources()
@ -614,19 +616,18 @@ GO`,
const handleOpenCopyDialog = () => {
if (!state.selectedDataSource) return
if (selectedExplorerObjects.length === 0) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{'Lutfen kopyalamak icin en az bir obje secin.'}
</Notification>,
{ placement: 'top-center' },
)
return
}
setCopyDialogMode('objects')
setCopyTargetDataSources([])
setOverwriteIfExists(false)
setSqlScriptForCopy('')
setShowCopyDialog(true)
// Eğer seçili obje yoksa uyarı göster
if (selectedExplorerObjects.length === 0) {
// SQL mode'da obje seçimi zorunlu değil, object mode'da zorunlu
// Bu uyarı sadece object mode'da gerekirse
return
}
}
const handleCopyObjects = async () => {
@ -665,7 +666,7 @@ GO`,
objectFullName: obj.fullName,
objectType: obj.objectType,
status: 'error',
message: 'Kaynak objenin scripti olusturulamadi.',
message: translate('::App.Platform.CreateScriptFailed'),
})
continue
}
@ -706,7 +707,7 @@ GO`,
objectFullName: obj.fullName,
objectType: obj.objectType,
status: 'success',
message: 'Basariyla kopyalandi.',
message: translate('::App.Platform.CopyCompleted'),
})
} catch (error: any) {
results.push({
@ -734,7 +735,8 @@ GO`,
: translate('::App.Platform.Success')
}
>
{`Kopyalama tamamlandi. Basarili: ${successCount}, Atlanan: ${skippedCount}, Hata: ${errorCount}`}
{translate('::App.Platform.CopyCompleted') ||
`translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}, ${translate('::App.Platform.Skipped')}: ${skippedCount}`}
</Notification>,
{ placement: 'top-center' },
)
@ -748,6 +750,82 @@ GO`,
}
}
const handleExecuteDirectSql = async () => {
if (!sqlScriptForCopy?.trim()) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseEnterQuery')}
</Notification>,
{ placement: 'top-center' },
)
return
}
if (copyTargetDataSources.length === 0) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseSelectAtLeastOneTarget')}
</Notification>,
{ placement: 'top-center' },
)
return
}
setIsCopyingObjects(true)
const results: SqlCopyResultItem[] = []
try {
for (const targetDataSource of copyTargetDataSources) {
try {
await sqlObjectManagerService.executeQuery({
queryText: sqlScriptForCopy,
dataSourceCode: targetDataSource,
})
results.push({
targetDataSource,
objectFullName: 'SQL Script',
objectType: 'script' as any,
status: 'success',
message: 'Basariyla calistirildi.',
})
} catch (error: any) {
results.push({
targetDataSource,
objectFullName: 'SQL Script',
objectType: 'script' as any,
status: 'error',
message: error.response?.data?.error?.message || 'Calistirma basarisiz.',
})
}
}
const successCount = results.filter((x) => x.status === 'success').length
const errorCount = results.filter((x) => x.status === 'error').length
const notificationType = errorCount > 0 ? 'warning' : 'success'
toast.push(
<Notification
type={notificationType}
title={
errorCount > 0
? translate('::App.Platform.Warning')
: translate('::App.Platform.Success')
}
>
{translate('::App.Platform.ExecutionCompleted') ||
`translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}`}
</Notification>,
{ placement: 'top-center' },
)
setCopyResults(results)
setShowCopyResultDialog(true)
setShowCopyDialog(false)
} finally {
setIsCopyingObjects(false)
}
}
const availableTargetDataSourceCodes = state.dataSources
.filter((d) => d.code && d.code !== state.selectedDataSource)
.map((d) => d.code || '')
@ -805,8 +883,12 @@ GO`,
size="sm"
variant="default"
onClick={handleOpenCopyDialog}
disabled={!state.selectedDataSource || selectedExplorerObjects.length === 0}
disabled={!state.selectedDataSource}
className="shadow-sm"
title={
translate('::App.Platform.CopyOrExecuteSql') ||
'Seçili nesneleri kopyala veya SQL script calistir'
}
>
{translate('::App.Platform.CopySelectedObjects') || 'Copy Selected Objects'}
</Button>
@ -978,7 +1060,37 @@ GO`,
>
<div className="flex h-full max-h-[85vh] flex-col">
<h5 className="mb-3">{translate('::App.Platform.CopySelectedObjects')}</h5>
{/* Mode Tabs */}
<div className="flex gap-2 mb-4 border-b">
<button
onClick={() => setCopyDialogMode('objects')}
className={`px-4 py-2 font-medium text-sm border-b-2 transition ${
copyDialogMode === 'objects'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-600 hover:text-gray-900'
}`}
disabled={isCopyingObjects}
>
{translate('::App.Platform.CopyObjects') || 'Nesneleri Kopyala'}
</button>
<button
onClick={() => setCopyDialogMode('sql')}
className={`px-4 py-2 font-medium text-sm border-b-2 transition ${
copyDialogMode === 'sql'
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-600 hover:text-gray-900'
}`}
disabled={isCopyingObjects}
>
{translate('::App.Platform.DirectSqlScript') || 'Direkt SQL Script'}
</button>
</div>
<div className="flex-1 overflow-y-auto pr-1">
{copyDialogMode === 'objects' ? (
<>
{/* Objects Mode */}
<div className="mb-2 flex items-center justify-between gap-4">
<p className="text-sm text-gray-600 dark:text-gray-400 mb-0">
{translate('::App.Platform.SourceDataSource')}:{' '}
@ -996,11 +1108,18 @@ GO`,
</div>
<div className="mb-4 max-h-36 overflow-auto border rounded p-2">
{selectedExplorerObjects.map((obj) => (
{selectedExplorerObjects.length === 0 ? (
<p className="text-sm text-gray-500">
{translate('::App.Platform.NoObjectSelected') ||
'Secili nesne yok. Lutfen Explorer alanından bir nesne secin.'}
</p>
) : (
selectedExplorerObjects.map((obj) => (
<div key={obj.id} className="text-sm py-0.5">
{obj.objectType.toUpperCase()} - {obj.fullName}
</div>
))}
))
)}
</div>
<div className="mb-4">
@ -1019,7 +1138,10 @@ GO`,
{availableTargetDataSourceCodes.map((code) => {
const checked = copyTargetDataSources.includes(code)
return (
<label key={code} className="flex items-center gap-2 text-sm cursor-pointer">
<label
key={code}
className="flex items-center gap-2 text-sm cursor-pointer"
>
<input
type="checkbox"
checked={checked}
@ -1036,7 +1158,65 @@ GO`,
})}
</div>
</div>
</>
) : (
<>
{/* SQL Mode */}
<div className="mb-2">
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
{translate('::App.Platform.SourceDataSource')}:{' '}
<strong>{state.selectedDataSource}</strong>
</p>
</div>
<div className="mb-4 border rounded bg-white dark:bg-gray-800 h-64 overflow-hidden">
<SqlEditor
value={sqlScriptForCopy}
onChange={(value) => setSqlScriptForCopy(value || '')}
readOnly={isCopyingObjects}
/>
</div>
<div className="mb-4">
<div className="text-sm font-medium mb-2">
{translate('::App.Platform.TargetDataSources')}
</div>
<label className="flex items-center gap-2 text-sm cursor-pointer mb-2">
<input
type="checkbox"
checked={allTargetsSelected}
onChange={(e) => handleToggleSelectAllTargets(e.target.checked)}
/>
<span>{translate('::App.Platform.SelectAllTargets') || 'Tumunu sec'}</span>
</label>
<div className="max-h-44 overflow-auto border-t pt-2">
{availableTargetDataSourceCodes.map((code) => {
const checked = copyTargetDataSources.includes(code)
return (
<label
key={code}
className="flex items-center gap-2 text-sm cursor-pointer"
>
<input
type="checkbox"
checked={checked}
onChange={(e) => {
setCopyTargetDataSources((prev) => {
if (e.target.checked) return [...prev, code]
return prev.filter((x) => x !== code)
})
}}
/>
<span>{code}</span>
</label>
)
})}
</div>
</div>
</>
)}
</div>
<div className="mt-2 flex justify-end gap-2 border-t pt-3">
<Button
variant="plain"
@ -1047,11 +1227,18 @@ GO`,
</Button>
<Button
variant="solid"
onClick={handleCopyObjects}
onClick={copyDialogMode === 'objects' ? handleCopyObjects : handleExecuteDirectSql}
loading={isCopyingObjects}
disabled={copyTargetDataSources.length === 0 || selectedExplorerObjects.length === 0}
disabled={
copyTargetDataSources.length === 0 ||
(copyDialogMode === 'objects'
? selectedExplorerObjects.length === 0
: !sqlScriptForCopy?.trim())
}
>
{translate('::Copy')}
{copyDialogMode === 'objects'
? translate('::Copy')
: translate('::App.Platform.Execute') || 'Calistir'}
</Button>
</div>
</div>
@ -1137,11 +1324,21 @@ GO`,
<table className="hidden md:table w-full table-fixed text-sm">
<thead className="bg-gray-50 dark:bg-gray-700 sticky top-0 z-10">
<tr>
<th className="w-[110px] text-left px-3 py-2 border-b">{translate('::App.Platform.Status')}</th>
<th className="w-[270px] text-left px-3 py-2 border-b">{translate('::App.Platform.Object')}</th>
<th className="w-[90px] text-left px-3 py-2 border-b">{translate('::App.Platform.Type')}</th>
<th className="w-[140px] text-left px-3 py-2 border-b">{translate('::App.Platform.Target')}</th>
<th className="text-left px-3 py-2 border-b">{translate('::App.Platform.Message')}</th>
<th className="w-[110px] text-left px-3 py-2 border-b">
{translate('::App.Platform.Status')}
</th>
<th className="w-[270px] text-left px-3 py-2 border-b">
{translate('::App.Platform.Object')}
</th>
<th className="w-[90px] text-left px-3 py-2 border-b">
{translate('::App.Platform.Type')}
</th>
<th className="w-[140px] text-left px-3 py-2 border-b">
{translate('::App.Platform.Target')}
</th>
<th className="text-left px-3 py-2 border-b">
{translate('::App.Platform.Message')}
</th>
</tr>
</thead>
<tbody>
@ -1209,7 +1406,7 @@ GO`,
<div className="flex justify-end gap-2 mt-4">
<Button variant="solid" onClick={() => setShowCopyResultDialog(false)}>
Kapat
{translate('::App.Platform.Close')}
</Button>
</div>
</Dialog>