Genel değişiklikler
This commit is contained in:
parent
233c9b7502
commit
e73cdbbb7a
16 changed files with 663 additions and 384 deletions
|
|
@ -220,7 +220,7 @@ public static class LookupQueryValues
|
||||||
$"\"Name\" AS \"Name\" " +
|
$"\"Name\" AS \"Name\" " +
|
||||||
$"FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.JobPosition))}\" " +
|
$"FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.JobPosition))}\" " +
|
||||||
$"WHERE " +
|
$"WHERE " +
|
||||||
$"(\"DepartmentId\" = @param0 OR @param0 IS NULL) " +
|
$"(\"DepartmentId\" = @param0 OR \"ParentId\" IS NULL OR @param0 IS NULL) " +
|
||||||
$"AND \"IsDeleted\" = 'false' " +
|
$"AND \"IsDeleted\" = 'false' " +
|
||||||
$"ORDER BY \"Name\";";
|
$"ORDER BY \"Name\";";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4026,7 +4026,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
SearchPanelJson = DefaultSearchPanelJson,
|
SearchPanelJson = DefaultSearchPanelJson,
|
||||||
GroupPanelJson = DefaultGroupPanelJson,
|
GroupPanelJson = DefaultGroupPanelJson,
|
||||||
SelectionJson = DefaultSelectionSingleJson,
|
SelectionJson = DefaultSelectionSingleJson,
|
||||||
ColumnOptionJson = DefaultColumnOptionJson(false),
|
ColumnOptionJson = DefaultColumnOptionJson(),
|
||||||
PermissionJson = DefaultPermissionJson(listFormName),
|
PermissionJson = DefaultPermissionJson(listFormName),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 950, 650, true, true, true, true, false),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 950, 650, true, true, true, true, false),
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
@apply p-6 rounded-lg shadow-xl sm:my-16 relative h-full bg-white dark:bg-gray-800;
|
max-height: calc(100vh - 2rem);
|
||||||
|
@apply p-6 rounded-lg shadow-xl my-4 relative bg-white dark:bg-gray-800 flex flex-col overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@screen sm {
|
||||||
|
.dialog-content {
|
||||||
|
max-height: calc(100vh - 8rem);
|
||||||
|
@apply my-16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header,
|
||||||
|
.dialog-footer {
|
||||||
|
@apply flex-shrink-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-body {
|
||||||
|
@apply flex-1 min-h-0 overflow-y-auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-content.maximized {
|
.dialog-content.maximized {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
height: 100vh !important;
|
height: 100vh !important;
|
||||||
|
max-height: 100vh !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,9 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
|
||||||
{!selectedFile ? (
|
{!selectedFile ? (
|
||||||
<div
|
<div
|
||||||
className={`relative border-2 border-dashed rounded-lg p-3 transition-all duration-200 ${
|
className={`relative border-2 border-dashed rounded-lg p-3 transition-all duration-200 ${
|
||||||
dragActive ? 'border-blue-400 bg-blue-50' : 'border-slate-300 hover:border-slate-400'
|
dragActive
|
||||||
|
? 'border-blue-400 bg-blue-50 dark:bg-blue-950/30'
|
||||||
|
: 'border-slate-300 hover:border-slate-400 dark:border-slate-600 dark:hover:border-slate-500'
|
||||||
} ${loading ? 'opacity-50 pointer-events-none' : ''}`}
|
} ${loading ? 'opacity-50 pointer-events-none' : ''}`}
|
||||||
onDragEnter={handleDrag}
|
onDragEnter={handleDrag}
|
||||||
onDragLeave={handleDrag}
|
onDragLeave={handleDrag}
|
||||||
|
|
@ -117,35 +119,39 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
|
||||||
|
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<FaUpload
|
<FaUpload
|
||||||
className={`mx-auto h-3 w-3 ${dragActive ? 'text-blue-500' : 'text-slate-400'}`}
|
className={`mx-auto h-3 w-3 ${dragActive ? 'text-blue-500 dark:text-blue-400' : 'text-slate-400 dark:text-slate-500'}`}
|
||||||
/>
|
/>
|
||||||
<div className="text-lg font-medium text-slate-700 mb-2">
|
<div className="text-lg font-medium text-slate-700 dark:text-slate-200 mb-2">
|
||||||
{dragActive
|
{dragActive
|
||||||
? translate('::App.Listforms.ImportManager.DropHere')
|
? translate('::App.Listforms.ImportManager.DropHere')
|
||||||
: translate('::App.Listforms.ImportManager.UploadYourFile')}
|
: translate('::App.Listforms.ImportManager.UploadYourFile')}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-slate-500 mb-4">
|
<p className="text-slate-500 dark:text-slate-400 mb-4">
|
||||||
{translate('::App.Listforms.ImportManager.DragOrClick')}
|
{translate('::App.Listforms.ImportManager.DragOrClick')}
|
||||||
</p>
|
</p>
|
||||||
<div className="text-sm text-slate-400">
|
<div className="text-sm text-slate-400 dark:text-slate-500">
|
||||||
{translate('::App.Listforms.ImportManager.SupportedFormats')}{' '}
|
{translate('::App.Listforms.ImportManager.SupportedFormats')}{' '}
|
||||||
{acceptedFormats.join(', ')} • Max size: {maxSize}MB
|
{acceptedFormats.join(', ')} • Max size: {maxSize}MB
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="border border-slate-200 rounded-lg p-4">
|
<div className="border border-slate-200 dark:border-slate-700 rounded-lg p-4 dark:bg-slate-900/40">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-3 min-w-0 flex-1">
|
<div className="flex items-center space-x-3 min-w-0 flex-1">
|
||||||
<FaFile className="w-8 h-8 text-blue-500 flex-shrink-0" />
|
<FaFile className="w-8 h-8 text-blue-500 flex-shrink-0" />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="font-medium text-slate-800 truncate">{selectedFile.name}</div>
|
<div className="font-medium text-slate-800 dark:text-slate-100 truncate">
|
||||||
<div className="text-sm text-slate-500">{formatFileSize(selectedFile.size)}</div>
|
{selectedFile.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-slate-500 dark:text-slate-400">
|
||||||
|
{formatFileSize(selectedFile.size)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={clearFile}
|
onClick={clearFile}
|
||||||
className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded-lg transition-colors"
|
className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:text-slate-500 dark:hover:text-slate-300 dark:hover:bg-slate-800 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
<FaTimes className="w-4 h-4" />
|
<FaTimes className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -154,9 +160,9 @@ export const FileUploadArea: React.FC<FileUploadAreaProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="flex items-center space-x-2 p-3 bg-red-50 border border-red-200 rounded-lg">
|
<div className="flex items-center space-x-2 p-3 bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900/60 rounded-lg">
|
||||||
<FaRegCircle className="w-5 h-5 text-red-500" />
|
<FaRegCircle className="w-5 h-5 text-red-500" />
|
||||||
<span className="text-red-700">{error}</span>
|
<span className="text-red-700 dark:text-red-300">{error}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,17 +195,17 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'uploaded':
|
case 'uploaded':
|
||||||
case 'executed':
|
case 'executed':
|
||||||
return 'bg-green-50 text-green-700 border-green-200'
|
return 'bg-green-50 text-green-700 border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-900/60'
|
||||||
case 'executed_with_errors':
|
case 'executed_with_errors':
|
||||||
return 'bg-orange-50 text-orange-700 border-orange-200'
|
return 'bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950/30 dark:text-orange-300 dark:border-orange-900/60'
|
||||||
case 'failed':
|
case 'failed':
|
||||||
case 'execute_failed':
|
case 'execute_failed':
|
||||||
return 'bg-red-50 text-red-700 border-red-200'
|
return 'bg-red-50 text-red-700 border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-900/60'
|
||||||
case 'processing':
|
case 'processing':
|
||||||
case 'validating':
|
case 'validating':
|
||||||
return 'bg-blue-50 text-blue-700 border-blue-200'
|
return 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-900/60'
|
||||||
default:
|
default:
|
||||||
return 'bg-yellow-50 text-yellow-700 border-yellow-200'
|
return 'bg-yellow-50 text-yellow-700 border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-300 dark:border-yellow-900/60'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,17 +269,17 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
const getExecuteStatusColor = (status: string) => {
|
const getExecuteStatusColor = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return 'text-green-600'
|
return 'text-green-600 dark:text-green-300'
|
||||||
case 'completed_with_errors':
|
case 'completed_with_errors':
|
||||||
return 'text-orange-600'
|
return 'text-orange-600 dark:text-orange-300'
|
||||||
case 'processing':
|
case 'processing':
|
||||||
return 'text-blue-600'
|
return 'text-blue-600 dark:text-blue-300'
|
||||||
case 'validating':
|
case 'validating':
|
||||||
return 'text-yellow-600'
|
return 'text-yellow-600 dark:text-yellow-300'
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return 'text-red-600'
|
return 'text-red-600 dark:text-red-300'
|
||||||
default:
|
default:
|
||||||
return 'text-slate-600'
|
return 'text-slate-600 dark:text-slate-400'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,7 +331,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full mt-4">
|
<div className="flex flex-col w-full mt-4">
|
||||||
{/* Navigation Tabs */}
|
{/* Navigation Tabs */}
|
||||||
<div className="flex space-x-1 mb-4 bg-white rounded-lg p-1 shadow-sm border border-slate-200 flex-shrink-0">
|
<div className="flex space-x-1 mb-4 bg-white dark:bg-slate-900 rounded-lg p-1 shadow-sm border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||||
{['import', 'preview', 'history'].map((tab) => (
|
{['import', 'preview', 'history'].map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab}
|
key={tab}
|
||||||
|
|
@ -339,7 +339,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
className={`px-3 py-2 rounded-md font-medium transition-all duration-200 flex items-center space-x-2 ${
|
className={`px-3 py-2 rounded-md font-medium transition-all duration-200 flex items-center space-x-2 ${
|
||||||
activeTab === tab
|
activeTab === tab
|
||||||
? 'bg-blue-500 text-white shadow-md'
|
? 'bg-blue-500 text-white shadow-md'
|
||||||
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50'
|
: 'text-slate-600 hover:text-slate-800 hover:bg-slate-50 dark:text-slate-300 dark:hover:text-slate-100 dark:hover:bg-slate-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab === 'import' && <FaUpload className="w-4 h-4" />}
|
{tab === 'import' && <FaUpload className="w-4 h-4" />}
|
||||||
|
|
@ -358,9 +358,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
{/* Template Generator - 2/3 width on large screens, full width on mobile */}
|
{/* Template Generator - 2/3 width on large screens, full width on mobile */}
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
|
||||||
<div className="px-3 py-3 border-b flex items-center justify-between">
|
<div className="px-3 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
|
||||||
<h3 className="text-xl font-semibold text-slate-800 flex items-center">
|
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100 flex items-center">
|
||||||
<FaDownload className="w-4 h-4 mr-2" />
|
<FaDownload className="w-4 h-4 mr-2" />
|
||||||
{translate('::App.Listforms.ImportManager.TemplateColumns')} (
|
{translate('::App.Listforms.ImportManager.TemplateColumns')} (
|
||||||
{editableColumns.length})
|
{editableColumns.length})
|
||||||
|
|
@ -371,10 +371,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
<button
|
<button
|
||||||
onClick={() => generateTemplate('excel')}
|
onClick={() => generateTemplate('excel')}
|
||||||
disabled={generating}
|
disabled={generating}
|
||||||
className="flex items-center gap-1.5 px-3 py-1.5 border border-green-200 rounded-md hover:border-green-300 hover:bg-green-50 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white text-xs"
|
className="flex items-center gap-1.5 px-3 py-1.5 border border-green-200 dark:border-green-900/60 rounded-md hover:border-green-300 hover:bg-green-50 dark:hover:bg-green-950/30 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-slate-900 text-xs"
|
||||||
>
|
>
|
||||||
<FaFileExcel className="w-3.5 h-3.5 text-green-500 group-hover:scale-110 transition-transform" />
|
<FaFileExcel className="w-3.5 h-3.5 text-green-500 group-hover:scale-110 transition-transform" />
|
||||||
<span className="font-medium text-slate-700">
|
<span className="font-medium text-slate-700 dark:text-slate-200">
|
||||||
{translate('::App.Listforms.ImportManager.ExcelTemplate')}
|
{translate('::App.Listforms.ImportManager.ExcelTemplate')}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -382,10 +382,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
<button
|
<button
|
||||||
onClick={() => generateTemplate('csv')}
|
onClick={() => generateTemplate('csv')}
|
||||||
disabled={generating}
|
disabled={generating}
|
||||||
className="flex items-center gap-1.5 px-3 py-1.5 border border-blue-200 rounded-md hover:border-blue-300 hover:bg-blue-50 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white text-xs"
|
className="flex items-center gap-1.5 px-3 py-1.5 border border-blue-200 dark:border-blue-900/60 rounded-md hover:border-blue-300 hover:bg-blue-50 dark:hover:bg-blue-950/30 transition-all duration-200 group disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-slate-900 text-xs"
|
||||||
>
|
>
|
||||||
<FaFileAlt className="w-3.5 h-3.5 text-blue-500 group-hover:scale-110 transition-transform" />
|
<FaFileAlt className="w-3.5 h-3.5 text-blue-500 group-hover:scale-110 transition-transform" />
|
||||||
<span className="font-medium text-slate-700">
|
<span className="font-medium text-slate-700 dark:text-slate-200">
|
||||||
{translate('::App.Listforms.ImportManager.CsvTemplate')}
|
{translate('::App.Listforms.ImportManager.CsvTemplate')}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -394,38 +394,38 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
|
|
||||||
<div className="max-h-96 overflow-y-auto">
|
<div className="max-h-96 overflow-y-auto">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead className="bg-slate-100 sticky top-0">
|
<thead className="bg-slate-100 dark:bg-slate-800 sticky top-0">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
|
||||||
{translate('::App.Listform.ListformField.Column')}
|
{translate('::App.Listform.ListformField.Column')}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
|
||||||
{translate('::ListForms.ListFormEdit.Type')}
|
{translate('::ListForms.ListFormEdit.Type')}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
|
||||||
{translate('::App.Required')}
|
{translate('::App.Required')}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase">
|
||||||
{translate('::Abp.Mailing.Default')}
|
{translate('::Abp.Mailing.Default')}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-100">
|
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
|
||||||
{editableColumns.map((column: any) => (
|
{editableColumns.map((column: any) => (
|
||||||
<tr key={column.fieldName} className="hover:bg-slate-50">
|
<tr key={column.fieldName} className="hover:bg-slate-50 dark:hover:bg-slate-800/70">
|
||||||
<td className="px-2 py-2 font-medium text-slate-800">
|
<td className="px-2 py-2 font-medium text-slate-800 dark:text-slate-100">
|
||||||
{column.fieldName}
|
{column.fieldName}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-slate-600">
|
<td className="px-4 py-2 text-slate-600 dark:text-slate-300">
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 rounded text-xs font-medium ${
|
className={`px-2 py-1 rounded text-xs font-medium ${
|
||||||
column.dataType === 'string'
|
column.dataType === 'string'
|
||||||
? 'bg-blue-100 text-blue-800'
|
? 'bg-blue-100 text-blue-800 dark:bg-blue-950/40 dark:text-blue-300'
|
||||||
: column.dataType === 'number'
|
: column.dataType === 'number'
|
||||||
? 'bg-green-100 text-green-800'
|
? 'bg-green-100 text-green-800 dark:bg-green-950/40 dark:text-green-300'
|
||||||
: column.dataType === 'boolean'
|
: column.dataType === 'boolean'
|
||||||
? 'bg-purple-100 text-purple-800'
|
? 'bg-purple-100 text-purple-800 dark:bg-purple-950/40 dark:text-purple-300'
|
||||||
: 'bg-orange-100 text-orange-800'
|
: 'bg-orange-100 text-orange-800 dark:bg-orange-950/40 dark:text-orange-300'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{column.dataType}
|
{column.dataType}
|
||||||
|
|
@ -435,16 +435,16 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
{column.validationRuleDto.some(
|
{column.validationRuleDto.some(
|
||||||
(rule: any) => rule.type === 'required',
|
(rule: any) => rule.type === 'required',
|
||||||
) ? (
|
) ? (
|
||||||
<span className="text-red-500 font-medium">
|
<span className="text-red-500 dark:text-red-300 font-medium">
|
||||||
{translate('::App.Listforms.ImportManager.Yes')}
|
{translate('::App.Listforms.ImportManager.Yes')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-slate-400">
|
<span className="text-slate-400 dark:text-slate-500">
|
||||||
{translate('::App.Listforms.ImportManager.No')}
|
{translate('::App.Listforms.ImportManager.No')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-slate-600 text-sm">
|
<td className="px-4 py-2 text-slate-600 dark:text-slate-300 text-sm">
|
||||||
{typeof column.defaultValue === 'object'
|
{typeof column.defaultValue === 'object'
|
||||||
? JSON.stringify(column.defaultValue)
|
? JSON.stringify(column.defaultValue)
|
||||||
: column.defaultValue}
|
: column.defaultValue}
|
||||||
|
|
@ -458,7 +458,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
{generating && (
|
{generating && (
|
||||||
<div className="flex items-center justify-center py-4">
|
<div className="flex items-center justify-center py-4">
|
||||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500"></div>
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500"></div>
|
||||||
<span className="ml-2 text-slate-600">
|
<span className="ml-2 text-slate-600 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.GeneratingTemplate')}
|
{translate('::App.Listforms.ImportManager.GeneratingTemplate')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -468,8 +468,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
|
|
||||||
{/* File Upload - 1/3 width on large screens, full width on mobile */}
|
{/* File Upload - 1/3 width on large screens, full width on mobile */}
|
||||||
<div className="lg:col-span-1">
|
<div className="lg:col-span-1">
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-4 h-full">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-4 h-full">
|
||||||
<h2 className="text-xl font-semibold text-slate-800 mb-4 flex items-center">
|
<h2 className="text-xl font-semibold text-slate-800 dark:text-slate-100 mb-4 flex items-center">
|
||||||
<FaUpload className="w-5 h-5 mr-2 text-green-500" />
|
<FaUpload className="w-5 h-5 mr-2 text-green-500" />
|
||||||
{translate('::App.Listforms.ImportManager.UploadData')}
|
{translate('::App.Listforms.ImportManager.UploadData')}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
@ -506,9 +506,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-12">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-12">
|
||||||
<div className="text-center text-slate-500">
|
<div className="text-center text-slate-500 dark:text-slate-400">
|
||||||
<FaEye className="w-16 h-16 mx-auto mb-4 text-slate-300" />
|
<FaEye className="w-16 h-16 mx-auto mb-4 text-slate-300 dark:text-slate-600" />
|
||||||
<div className="text-xl font-medium mb-2">
|
<div className="text-xl font-medium mb-2">
|
||||||
{translate('::App.Listforms.ImportManager.NoDataToPreview')}
|
{translate('::App.Listforms.ImportManager.NoDataToPreview')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -520,22 +520,22 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'history' && (
|
{activeTab === 'history' && (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
|
||||||
<div className="p-3 border-b border-slate-200">
|
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
|
||||||
<h2 className="text-xl font-semibold text-slate-800 flex items-center">
|
<h2 className="text-xl font-semibold text-slate-800 dark:text-slate-100 flex items-center">
|
||||||
<FaClock className="w-5 h-5 mr-2 text-indigo-500" />
|
<FaClock className="w-5 h-5 mr-2 text-indigo-500" />
|
||||||
{translate('::App.Listforms.ImportManager.ImportHistory')}
|
{translate('::App.Listforms.ImportManager.ImportHistory')}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="divide-y divide-slate-100">
|
<div className="divide-y divide-slate-100 dark:divide-slate-800">
|
||||||
{importHistory.map((session) => (
|
{importHistory.map((session) => (
|
||||||
<div
|
<div
|
||||||
key={session.id}
|
key={session.id}
|
||||||
className={`p-2 transition-colors border-l-4 ${
|
className={`p-2 transition-colors border-l-4 ${
|
||||||
currentSession?.id === session.id
|
currentSession?.id === session.id
|
||||||
? 'bg-blue-50 border-l-blue-500 hover:bg-blue-100'
|
? 'bg-blue-50 border-l-blue-500 hover:bg-blue-100 dark:bg-blue-950/30 dark:hover:bg-blue-950/40'
|
||||||
: 'border-l-transparent hover:bg-slate-50'
|
: 'border-l-transparent hover:bg-slate-50 dark:hover:bg-slate-800/70'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
@ -543,13 +543,15 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
{getStatusIcon(session.status)}
|
{getStatusIcon(session.status)}
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-slate-800">{session.blobName}</div>
|
<div className="font-medium text-slate-800 dark:text-slate-100">
|
||||||
<div className="text-sm text-slate-500">
|
{session.blobName}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-slate-500 dark:text-slate-400">
|
||||||
{new Date(session.creationTime).toLocaleString()}
|
{new Date(session.creationTime).toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{currentSession?.id === session.id && (
|
{currentSession?.id === session.id && (
|
||||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-950/40 dark:text-blue-300">
|
||||||
{translate('::App.Status.Active')}
|
{translate('::App.Status.Active')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -558,7 +560,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<div className="text-sm font-medium text-slate-800">
|
<div className="text-sm font-medium text-slate-800 dark:text-slate-100">
|
||||||
{session.totalRows} {translate('::App.Listforms.ImportManager.TotalRows')}
|
{session.totalRows} {translate('::App.Listforms.ImportManager.TotalRows')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -576,8 +578,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
onClick={() => toggleSessionExecutes(session.id)}
|
onClick={() => toggleSessionExecutes(session.id)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
expandedSessions.has(session.id)
|
expandedSessions.has(session.id)
|
||||||
? 'text-red-500 bg-red-50 hover:text-red-600 hover:bg-red-100'
|
? 'text-red-500 bg-red-50 hover:text-red-600 hover:bg-red-100 dark:bg-red-950/30 dark:text-red-300 dark:hover:bg-red-950/40'
|
||||||
: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100'
|
: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:text-slate-500 dark:hover:text-slate-300 dark:hover:bg-slate-800'
|
||||||
}`}
|
}`}
|
||||||
title={translate('::App.Listforms.ImportManager.ViewExecutionDetails')}
|
title={translate('::App.Listforms.ImportManager.ViewExecutionDetails')}
|
||||||
>
|
>
|
||||||
|
|
@ -607,7 +609,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="p-2 rounded-lg transition-colors text-slate-400 hover:text-blue-500 hover:bg-blue-50"
|
className="p-2 rounded-lg transition-colors text-slate-400 hover:text-blue-500 hover:bg-blue-50 dark:text-slate-500 dark:hover:text-blue-300 dark:hover:bg-blue-950/30"
|
||||||
title={translate('::App.Listforms.ImportManager.RefreshExecutionDetails')}
|
title={translate('::App.Listforms.ImportManager.RefreshExecutionDetails')}
|
||||||
>
|
>
|
||||||
<FaSync className="w-4 h-4" />
|
<FaSync className="w-4 h-4" />
|
||||||
|
|
@ -623,8 +625,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
disabled={currentSession?.id === session.id}
|
disabled={currentSession?.id === session.id}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
currentSession?.id === session.id
|
currentSession?.id === session.id
|
||||||
? 'text-slate-300 cursor-not-allowed'
|
? 'text-slate-300 dark:text-slate-600 cursor-not-allowed'
|
||||||
: 'text-slate-400 hover:text-red-500 hover:bg-red-50'
|
: 'text-slate-400 hover:text-red-500 hover:bg-red-50 dark:text-slate-500 dark:hover:text-red-300 dark:hover:bg-red-950/30'
|
||||||
}`}
|
}`}
|
||||||
title={
|
title={
|
||||||
currentSession?.id === session.id
|
currentSession?.id === session.id
|
||||||
|
|
@ -645,10 +647,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
session.status === 'executed_with_errors' ||
|
session.status === 'executed_with_errors' ||
|
||||||
session.status === 'failed' ||
|
session.status === 'failed' ||
|
||||||
session.status === 'execute_failed'
|
session.status === 'execute_failed'
|
||||||
? 'bg-red-50 text-red-700 border border-red-200'
|
? 'bg-red-50 text-red-700 border border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-900/60'
|
||||||
: session.status === 'uploaded'
|
: session.status === 'uploaded'
|
||||||
? 'bg-blue-50 text-blue-700 border border-blue-200'
|
? 'bg-blue-50 text-blue-700 border border-blue-200 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-900/60'
|
||||||
: 'bg-green-50 text-green-700 border border-green-200'
|
: 'bg-green-50 text-green-700 border border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-900/60'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="mt-0.5 flex-shrink-0">
|
<span className="mt-0.5 flex-shrink-0">
|
||||||
|
|
@ -668,10 +670,10 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
|
|
||||||
{/* Execute Details Section */}
|
{/* Execute Details Section */}
|
||||||
{expandedSessions.has(session.id) && (
|
{expandedSessions.has(session.id) && (
|
||||||
<div className="mt-3 bg-gradient-to-r from-indigo-50 to-blue-50 border border-indigo-100 rounded-lg shadow-sm hover:shadow-md transition-shadow">
|
<div className="mt-3 bg-gradient-to-r from-indigo-50 to-blue-50 dark:from-slate-800 dark:to-slate-800 border border-indigo-100 dark:border-slate-700 rounded-lg shadow-sm hover:shadow-md transition-shadow">
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
{loadingExecutes.has(session.id) ? (
|
{loadingExecutes.has(session.id) ? (
|
||||||
<div className="flex items-center space-x-2 text-slate-500 py-2">
|
<div className="flex items-center space-x-2 text-slate-500 dark:text-slate-400 py-2">
|
||||||
<FaSync className="w-4 h-4 animate-spin" />
|
<FaSync className="w-4 h-4 animate-spin" />
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{translate('::App.Listforms.ImportManager.LoadingExecutionDetails')}
|
{translate('::App.Listforms.ImportManager.LoadingExecutionDetails')}
|
||||||
|
|
@ -681,38 +683,38 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
sessionExecutes[session.id].length > 0 ? (
|
sessionExecutes[session.id].length > 0 ? (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{sessionExecutes[session.id].map((execute) => (
|
{sessionExecutes[session.id].map((execute) => (
|
||||||
<div key={execute.id} className="p-3 rounded-lg">
|
<div key={execute.id} className="p-3 rounded-lg dark:bg-slate-900/40">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
{/* Sol: Tarih */}
|
{/* Sol: Tarih */}
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<div className="text-lg text-slate-500">
|
<div className="text-lg text-slate-500 dark:text-slate-400">
|
||||||
{new Date(execute.creationTime).toLocaleString()}
|
{new Date(execute.creationTime).toLocaleString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Orta: Executed, Valid, Errors */}
|
{/* Orta: Executed, Valid, Errors */}
|
||||||
<div className="flex items-center space-x-4 text-xs text-slate-600">
|
<div className="flex items-center space-x-4 text-xs text-slate-600 dark:text-slate-400">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="font-medium text-slate-800">
|
<div className="font-medium text-slate-800 dark:text-slate-100">
|
||||||
{execute.execRows}
|
{execute.execRows}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-500">
|
<div className="text-slate-500 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.Executed')}
|
{translate('::App.Listforms.ImportManager.Executed')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="font-medium text-green-600">
|
<div className="font-medium text-green-600 dark:text-green-300">
|
||||||
{execute.validRows}
|
{execute.validRows}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-500">
|
<div className="text-slate-500 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.Valid')}
|
{translate('::App.Listforms.ImportManager.Valid')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="font-medium text-red-600">
|
<div className="font-medium text-red-600 dark:text-red-300">
|
||||||
{execute.errorRows}
|
{execute.errorRows}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-500">
|
<div className="text-slate-500 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.Errors')}
|
{translate('::App.Listforms.ImportManager.Errors')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -737,7 +739,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleErrors(execute.id)}
|
onClick={() => toggleErrors(execute.id)}
|
||||||
className="flex items-center space-x-1 text-xs text-orange-600 hover:text-orange-700 font-medium"
|
className="flex items-center space-x-1 text-xs text-orange-600 hover:text-orange-700 dark:text-orange-300 dark:hover:text-orange-200 font-medium"
|
||||||
>
|
>
|
||||||
{expandedErrors.has(execute.id) ? (
|
{expandedErrors.has(execute.id) ? (
|
||||||
<FaChevronUp className="w-3 h-3" />
|
<FaChevronUp className="w-3 h-3" />
|
||||||
|
|
@ -751,26 +753,29 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{expandedErrors.has(execute.id) && (
|
{expandedErrors.has(execute.id) && (
|
||||||
<div className="mt-2 max-h-48 overflow-y-auto rounded border border-orange-200 bg-orange-50">
|
<div className="mt-2 max-h-48 overflow-y-auto rounded border border-orange-200 bg-orange-50 dark:border-orange-900/60 dark:bg-orange-950/30">
|
||||||
{parseErrors(execute.errorsJson).length > 0 ? (
|
{parseErrors(execute.errorsJson).length > 0 ? (
|
||||||
<table className="w-full text-xs">
|
<table className="w-full text-xs">
|
||||||
<thead className="bg-orange-100 sticky top-0">
|
<thead className="bg-orange-100 dark:bg-orange-950/50 sticky top-0">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-3 py-1 text-left font-medium text-orange-700 w-16">
|
<th className="px-3 py-1 text-left font-medium text-orange-700 dark:text-orange-300 w-16">
|
||||||
Satır
|
Satır
|
||||||
</th>
|
</th>
|
||||||
<th className="px-3 py-1 text-left font-medium text-orange-700">
|
<th className="px-3 py-1 text-left font-medium text-orange-700 dark:text-orange-300">
|
||||||
Hata Mesajı
|
Hata Mesajı
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-orange-100">
|
<tbody className="divide-y divide-orange-100 dark:divide-orange-900/50">
|
||||||
{parseErrors(execute.errorsJson).map((err, idx) => (
|
{parseErrors(execute.errorsJson).map((err, idx) => (
|
||||||
<tr key={idx} className="hover:bg-orange-100">
|
<tr
|
||||||
<td className="px-3 py-1 text-orange-700 font-medium">
|
key={idx}
|
||||||
|
className="hover:bg-orange-100 dark:hover:bg-orange-950/50"
|
||||||
|
>
|
||||||
|
<td className="px-3 py-1 text-orange-700 dark:text-orange-300 font-medium">
|
||||||
{err.row}
|
{err.row}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-1 text-slate-700">
|
<td className="px-3 py-1 text-slate-700 dark:text-slate-200">
|
||||||
{err.message}
|
{err.message}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -778,7 +783,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
) : (
|
) : (
|
||||||
<p className="px-3 py-2 text-orange-600">
|
<p className="px-3 py-2 text-orange-600 dark:text-orange-300">
|
||||||
Hata detayı mevcut değil.
|
Hata detayı mevcut değil.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -790,7 +795,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-slate-500 py-2">
|
<div className="text-sm text-slate-500 dark:text-slate-400 py-2">
|
||||||
{translate('::App.Listforms.ImportManager.NoExecutionRecords')}
|
{translate('::App.Listforms.ImportManager.NoExecutionRecords')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -801,8 +806,8 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{importHistory.length === 0 && (
|
{importHistory.length === 0 && (
|
||||||
<div className="p-12 text-center text-slate-500">
|
<div className="p-12 text-center text-slate-500 dark:text-slate-400">
|
||||||
<FaClock className="w-12 h-12 mx-auto mb-4 text-slate-300" />
|
<FaClock className="w-12 h-12 mx-auto mb-4 text-slate-300 dark:text-slate-600" />
|
||||||
<div className="text-lg font-medium mb-2">
|
<div className="text-lg font-medium mb-2">
|
||||||
{translate('::App.Listforms.ImportManager.NoImportHistory')}
|
{translate('::App.Listforms.ImportManager.NoImportHistory')}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -100,13 +100,13 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'uploaded':
|
case 'uploaded':
|
||||||
return 'text-green-600 bg-green-50 border-green-200'
|
return 'text-green-600 bg-green-50 border-green-200 dark:text-green-300 dark:bg-green-950/30 dark:border-green-900/60'
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return 'text-red-600 bg-red-50 border-red-200'
|
return 'text-red-600 bg-red-50 border-red-200 dark:text-red-300 dark:bg-red-950/30 dark:border-red-900/60'
|
||||||
case 'validating':
|
case 'validating':
|
||||||
return 'text-yellow-600 bg-yellow-50 border-yellow-200'
|
return 'text-yellow-600 bg-yellow-50 border-yellow-200 dark:text-yellow-300 dark:bg-yellow-950/30 dark:border-yellow-900/60'
|
||||||
default:
|
default:
|
||||||
return 'text-blue-600 bg-blue-50 border-blue-200'
|
return 'text-blue-600 bg-blue-50 border-blue-200 dark:text-blue-300 dark:bg-blue-950/30 dark:border-blue-900/60'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,14 +145,14 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="p-3 border-b border-slate-200">
|
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-center gap-4">
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-center gap-4">
|
||||||
{/* Başlık kısmı - Üstte mobile, solda desktop */}
|
{/* Başlık kısmı - Üstte mobile, solda desktop */}
|
||||||
<div className="flex items-center order-1 lg:order-none">
|
<div className="flex items-center order-1 lg:order-none">
|
||||||
<FaEye className="w-5 h-5 mr-2 text-blue-500" />
|
<FaEye className="w-5 h-5 mr-2 text-blue-500" />
|
||||||
<h3 className="text-xl font-semibold text-slate-800">
|
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100">
|
||||||
{translate('::App.Listforms.ImportManager.ImportPreviewTitle')}
|
{translate('::App.Listforms.ImportManager.ImportPreviewTitle')}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -160,9 +160,9 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
{/* İstatistik kartları - Mobile'da alt alta, desktop'ta yan yana */}
|
{/* İstatistik kartları - Mobile'da alt alta, desktop'ta yan yana */}
|
||||||
<div className="order-3 lg:order-none lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2">
|
<div className="order-3 lg:order-none lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2">
|
||||||
<div className="flex flex-col sm:flex-row justify-center gap-2">
|
<div className="flex flex-col sm:flex-row justify-center gap-2">
|
||||||
<div className="text-center px-3 py-1 bg-blue-50 rounded-full border border-blue-200 font-bold text-blue-600">
|
<div className="text-center px-3 py-1 bg-blue-50 dark:bg-blue-950/30 rounded-full border border-blue-200 dark:border-blue-900/60 font-bold text-blue-600 dark:text-blue-300">
|
||||||
{previewData?.rows?.length || session.totalRows || 0}{' '}
|
{previewData?.rows?.length || session.totalRows || 0}{' '}
|
||||||
<span className="text-xs text-blue-700">
|
<span className="text-xs text-blue-700 dark:text-blue-300">
|
||||||
{translate('::App.Listforms.ImportManager.TotalRows')}
|
{translate('::App.Listforms.ImportManager.TotalRows')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -184,16 +184,16 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
|
|
||||||
{/* Preview Data */}
|
{/* Preview Data */}
|
||||||
{previewData && previewData.headers && previewData.headers.length > 0 ? (
|
{previewData && previewData.headers && previewData.headers.length > 0 ? (
|
||||||
<div className="p-3 border-b border-slate-200">
|
<div className="p-3 border-b border-slate-200 dark:border-slate-700">
|
||||||
<h4 className="font-semibold text-slate-800 mb-4">
|
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-4">
|
||||||
{translate('::App.Listforms.ImportManager.DataPreviewTitle')}
|
{translate('::App.Listforms.ImportManager.DataPreviewTitle')}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="overflow-auto border border-slate-200 rounded-lg max-h-90">
|
<div className="overflow-auto border border-slate-200 dark:border-slate-700 rounded-lg max-h-90">
|
||||||
<table className="w-full text-sm min-w-full">
|
<table className="w-full text-sm min-w-full">
|
||||||
<thead className="bg-slate-50 sticky top-0 z-10">
|
<thead className="bg-slate-50 dark:bg-slate-800 sticky top-0 z-10">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-2 text-left font-medium text-slate-700 whitespace-nowrap w-12">
|
<th className="px-4 py-2 text-left font-medium text-slate-700 dark:text-slate-200 whitespace-nowrap w-12">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectAll}
|
checked={selectAll}
|
||||||
|
|
@ -204,19 +204,19 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
{previewData.headers.map((header: string, index: number) => (
|
{previewData.headers.map((header: string, index: number) => (
|
||||||
<th
|
<th
|
||||||
key={index}
|
key={index}
|
||||||
className="px-4 py-2 text-left font-medium text-slate-700 whitespace-nowrap"
|
className="px-4 py-2 text-left font-medium text-slate-700 dark:text-slate-200 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{header}
|
{header}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-100">
|
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
|
||||||
{previewData.rows.map((row: any[], rowIndex: number) => (
|
{previewData.rows.map((row: any[], rowIndex: number) => (
|
||||||
<tr
|
<tr
|
||||||
key={rowIndex}
|
key={rowIndex}
|
||||||
className={`hover:bg-slate-50 ${
|
className={`hover:bg-slate-50 dark:hover:bg-slate-800/70 ${
|
||||||
selectedRows.includes(rowIndex) ? 'bg-blue-50' : ''
|
selectedRows.includes(rowIndex) ? 'bg-blue-50 dark:bg-blue-950/30' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2">
|
||||||
|
|
@ -230,7 +230,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
{row.map((cell, cellIndex) => (
|
{row.map((cell, cellIndex) => (
|
||||||
<td
|
<td
|
||||||
key={cellIndex}
|
key={cellIndex}
|
||||||
className="px-4 py-2 text-slate-600 whitespace-nowrap max-w-xs truncate"
|
className="px-4 py-2 text-slate-600 dark:text-slate-300 whitespace-nowrap max-w-xs truncate"
|
||||||
>
|
>
|
||||||
{cell?.toString() || '-'}
|
{cell?.toString() || '-'}
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -242,25 +242,25 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : previewData && previewData.headers && previewData.headers.length === 0 ? (
|
) : previewData && previewData.headers && previewData.headers.length === 0 ? (
|
||||||
<div className="p-6 border-b border-slate-200">
|
<div className="p-6 border-b border-slate-200 dark:border-slate-700">
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<FaExclamationTriangle className="w-12 h-12 mx-auto mb-4 text-yellow-500" />
|
<FaExclamationTriangle className="w-12 h-12 mx-auto mb-4 text-yellow-500" />
|
||||||
<h4 className="font-semibold text-slate-800 mb-2">
|
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-2">
|
||||||
{translate('::App.Listforms.ImportManager.NoDataFoundTitle')}
|
{translate('::App.Listforms.ImportManager.NoDataFoundTitle')}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.NoDataFoundDescription')}
|
{translate('::App.Listforms.ImportManager.NoDataFoundDescription')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="p-6 border-b border-slate-200">
|
<div className="p-6 border-b border-slate-200 dark:border-slate-700">
|
||||||
<div className="text-center py-8">
|
<div className="text-center py-8">
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div>
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div>
|
||||||
<h4 className="font-semibold text-slate-800 mb-2">
|
<h4 className="font-semibold text-slate-800 dark:text-slate-100 mb-2">
|
||||||
{translate('::App.Listforms.ImportManager.LoadingPreviewTitle')}
|
{translate('::App.Listforms.ImportManager.LoadingPreviewTitle')}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.ImportManager.LoadingPreviewDescription')}
|
{translate('::App.Listforms.ImportManager.LoadingPreviewDescription')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -271,8 +271,8 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
{/* Success Message */}
|
{/* Success Message */}
|
||||||
{showSuccessMessage && lastExecutionResult && (
|
{showSuccessMessage && lastExecutionResult && (
|
||||||
<div className="mb-4 p-4 bg-green-50 border border-green-200 rounded-lg">
|
<div className="mb-4 p-4 bg-green-50 dark:bg-green-950/30 border border-green-200 dark:border-green-900/60 rounded-lg">
|
||||||
<div className="flex items-center space-x-2 text-green-700">
|
<div className="flex items-center space-x-2 text-green-700 dark:text-green-300">
|
||||||
<FaCheckCircle className="w-5 h-5" />
|
<FaCheckCircle className="w-5 h-5" />
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{translate('::App.Listforms.ImportManager.ImportProgress.Status.Uploaded')}{' '}
|
{translate('::App.Listforms.ImportManager.ImportProgress.Status.Uploaded')}{' '}
|
||||||
|
|
@ -286,7 +286,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
{selectedRows.length === 0 && (previewData?.rows?.length || 0) > 0 && (
|
{selectedRows.length === 0 && (previewData?.rows?.length || 0) > 0 && (
|
||||||
<div className="flex items-center space-x-2 text-orange-600">
|
<div className="flex items-center space-x-2 text-orange-600 dark:text-orange-300">
|
||||||
<FaExclamationTriangle className="w-5 h-5" />
|
<FaExclamationTriangle className="w-5 h-5" />
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{translate('::App.Listforms.ImportManager.SelectRowsWarning')}
|
{translate('::App.Listforms.ImportManager.SelectRowsWarning')}
|
||||||
|
|
@ -295,7 +295,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedRows.length > 0 && (
|
{selectedRows.length > 0 && (
|
||||||
<div className="flex items-center space-x-2 text-blue-600">
|
<div className="flex items-center space-x-2 text-blue-600 dark:text-blue-300">
|
||||||
<FaCheckCircle className="w-5 h-5" />
|
<FaCheckCircle className="w-5 h-5" />
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{selectedRows.length} {translate('::App.Listforms.ImportManager.RowsSelected')}
|
{selectedRows.length} {translate('::App.Listforms.ImportManager.RowsSelected')}
|
||||||
|
|
@ -304,7 +304,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(previewData?.rows?.length || 0) === 0 && (
|
{(previewData?.rows?.length || 0) === 0 && (
|
||||||
<div className="flex items-center space-x-2 text-red-600">
|
<div className="flex items-center space-x-2 text-red-600 dark:text-red-300">
|
||||||
<FaExclamationTriangle className="w-5 h-5" />
|
<FaExclamationTriangle className="w-5 h-5" />
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{translate('::App.Listforms.ImportManager.NoRowsAvailable')}
|
{translate('::App.Listforms.ImportManager.NoRowsAvailable')}
|
||||||
|
|
@ -314,7 +314,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3">
|
||||||
<button className="px-4 py-2 text-slate-600 hover:text-slate-800 hover:bg-slate-100 rounded-lg transition-colors flex items-center space-x-2">
|
<button className="px-4 py-2 text-slate-600 hover:text-slate-800 hover:bg-slate-100 dark:text-slate-300 dark:hover:text-slate-100 dark:hover:bg-slate-800 rounded-lg transition-colors flex items-center space-x-2">
|
||||||
<FaTimes className="w-4 h-4" />
|
<FaTimes className="w-4 h-4" />
|
||||||
<span>{translate('::Cancel')}</span>
|
<span>{translate('::Cancel')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -59,15 +59,17 @@ export const ImportProgress: React.FC<ImportProgressProps> = ({ session }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
|
<div className="bg-white dark:bg-slate-900 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 p-6">
|
||||||
<div className="text-center space-y-6">
|
<div className="text-center space-y-6">
|
||||||
{/* Status Icon */}
|
{/* Status Icon */}
|
||||||
<div className="flex justify-center">{getStatusIcon()}</div>
|
<div className="flex justify-center">{getStatusIcon()}</div>
|
||||||
|
|
||||||
{/* Status Message */}
|
{/* Status Message */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl font-semibold text-slate-800 mb-2">{getStatusMessage()}</h3>
|
<h3 className="text-xl font-semibold text-slate-800 dark:text-slate-100 mb-2">
|
||||||
<p className="text-slate-600">
|
{getStatusMessage()}
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-600 dark:text-slate-400">
|
||||||
{translate('::App.Listforms.Status.Processing')}{' '}
|
{translate('::App.Listforms.Status.Processing')}{' '}
|
||||||
{session.blobName}
|
{session.blobName}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -75,11 +77,11 @@ export const ImportProgress: React.FC<ImportProgressProps> = ({ session }) => {
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<div className="w-full max-w-md mx-auto">
|
<div className="w-full max-w-md mx-auto">
|
||||||
<div className="flex justify-between text-sm text-slate-600 mb-2">
|
<div className="flex justify-between text-sm text-slate-600 dark:text-slate-400 mb-2">
|
||||||
<span>{translate('::App.Listforms.ImportManager.ImportProgress.ProgressLabel')}</span>
|
<span>{translate('::App.Listforms.ImportManager.ImportProgress.ProgressLabel')}</span>
|
||||||
<span>{Math.round(getProgressPercentage())}%</span>
|
<span>{Math.round(getProgressPercentage())}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-slate-200 rounded-full h-2">
|
<div className="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2">
|
||||||
<div
|
<div
|
||||||
className="bg-blue-500 h-2 rounded-full transition-all duration-300 ease-out"
|
className="bg-blue-500 h-2 rounded-full transition-all duration-300 ease-out"
|
||||||
style={{ width: `${getProgressPercentage()}%` }}
|
style={{ width: `${getProgressPercentage()}%` }}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ const MobileNav = () => {
|
||||||
title="Navigation"
|
title="Navigation"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
bodyClass={classNames(navColor(), 'p-0')}
|
bodyClass={classNames(navColor(), 'p-0')}
|
||||||
width={330}
|
width={320}
|
||||||
placement={direction === DIR_RTL ? 'right' : 'left'}
|
placement={direction === DIR_RTL ? 'right' : 'left'}
|
||||||
onClose={onDrawerClose}
|
onClose={onDrawerClose}
|
||||||
onRequestClose={onDrawerClose}
|
onRequestClose={onDrawerClose}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,19 @@ const DialogContext = createContext<DialogContextValue>({ isMaximized: false })
|
||||||
|
|
||||||
export const useDialogContext = () => useContext(DialogContext)
|
export const useDialogContext = () => useContext(DialogContext)
|
||||||
|
|
||||||
|
const DialogHeader = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children?: ReactNode
|
||||||
|
className?: string
|
||||||
|
}) => (
|
||||||
|
<div className={classNames('dialog-header', className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
DialogHeader.displayName = 'Dialog.Header'
|
||||||
|
|
||||||
const DialogBody = ({
|
const DialogBody = ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
|
@ -24,14 +37,8 @@ const DialogBody = ({
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
}) => {
|
}) => {
|
||||||
const { isMaximized } = useContext(DialogContext)
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={classNames('dialog-body', className)}>
|
||||||
className={classNames(
|
|
||||||
isMaximized && 'flex-1 min-h-0 flex flex-col overflow-hidden',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -45,9 +52,8 @@ const DialogFooter = ({
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
}) => {
|
}) => {
|
||||||
const { isMaximized } = useContext(DialogContext)
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(isMaximized && 'flex-shrink-0', className)}>
|
<div className={classNames('dialog-footer', className)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -231,13 +237,7 @@ const Dialog = (props: DialogProps) => {
|
||||||
{closable && !showWindowControls && renderCloseButton}
|
{closable && !showWindowControls && renderCloseButton}
|
||||||
{closable && showWindowControls && renderWindowControls}
|
{closable && showWindowControls && renderWindowControls}
|
||||||
<DialogContext.Provider value={{ isMaximized }}>
|
<DialogContext.Provider value={{ isMaximized }}>
|
||||||
{isMaximized ? (
|
{children}
|
||||||
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
children
|
|
||||||
)}
|
|
||||||
</DialogContext.Provider>
|
</DialogContext.Provider>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
@ -247,11 +247,13 @@ const Dialog = (props: DialogProps) => {
|
||||||
Dialog.displayName = 'Dialog'
|
Dialog.displayName = 'Dialog'
|
||||||
|
|
||||||
type DialogType = typeof Dialog & {
|
type DialogType = typeof Dialog & {
|
||||||
|
Header: typeof DialogHeader
|
||||||
Body: typeof DialogBody
|
Body: typeof DialogBody
|
||||||
Footer: typeof DialogFooter
|
Footer: typeof DialogFooter
|
||||||
}
|
}
|
||||||
|
|
||||||
const DialogWithSubComponents = Dialog as DialogType
|
const DialogWithSubComponents = Dialog as DialogType
|
||||||
|
DialogWithSubComponents.Header = DialogHeader
|
||||||
DialogWithSubComponents.Body = DialogBody
|
DialogWithSubComponents.Body = DialogBody
|
||||||
DialogWithSubComponents.Footer = DialogFooter
|
DialogWithSubComponents.Footer = DialogFooter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { SelectBoxOption } from '@/types/shared'
|
||||||
import { Field, FieldProps, FormikErrors, FormikTouched, useFormikContext } from 'formik'
|
import { Field, FieldProps, FormikErrors, FormikTouched, useFormikContext } from 'formik'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { FaArrowLeft, FaArrowRight, FaPlus } from 'react-icons/fa'
|
import { FaArrowLeft, FaArrowRight, FaPlus, FaTable } from 'react-icons/fa'
|
||||||
import { dbSourceTypeOptions, listFormDefaultLayoutOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from '../edit/options'
|
import { dbSourceTypeOptions, listFormDefaultLayoutOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from '../edit/options'
|
||||||
import { ListFormWizardDto } from '@/proxy/admin/wizard/models'
|
import { ListFormWizardDto } from '@/proxy/admin/wizard/models'
|
||||||
import SqlTableDesignerDialog from '@/views/developerKit/SqlTableDesignerDialog'
|
import SqlTableDesignerDialog from '@/views/developerKit/SqlTableDesignerDialog'
|
||||||
|
|
@ -68,8 +68,35 @@ const WizardStep2 = ({
|
||||||
onNext,
|
onNext,
|
||||||
}: WizardStep2Props) => {
|
}: WizardStep2Props) => {
|
||||||
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
|
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
|
||||||
|
const [designTableData, setDesignTableData] = useState<{
|
||||||
|
schemaName: string
|
||||||
|
tableName: string
|
||||||
|
} | null>(null)
|
||||||
const formik = useFormikContext<ListFormWizardDto>()
|
const formik = useFormikContext<ListFormWizardDto>()
|
||||||
|
|
||||||
|
const selectedTable = values.selectCommand
|
||||||
|
? dbObjects?.tables.find(
|
||||||
|
(table) =>
|
||||||
|
table.tableName === values.selectCommand ||
|
||||||
|
table.fullName === values.selectCommand ||
|
||||||
|
`${table.schemaName}.${table.tableName}` === values.selectCommand,
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
const handleNewTable = () => {
|
||||||
|
setDesignTableData(null)
|
||||||
|
setShowTableDesignerDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDesignTable = () => {
|
||||||
|
if (!selectedTable) return
|
||||||
|
setDesignTableData({
|
||||||
|
schemaName: selectedTable.schemaName,
|
||||||
|
tableName: selectedTable.tableName,
|
||||||
|
})
|
||||||
|
setShowTableDesignerDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
const handleTableDeployed = async (table: { schemaName: string; tableName: string }) => {
|
const handleTableDeployed = async (table: { schemaName: string; tableName: string }) => {
|
||||||
await onDbObjectsRefresh(values.dataSourceCode)
|
await onDbObjectsRefresh(values.dataSourceCode)
|
||||||
formik.setFieldValue('selectCommand', table.tableName)
|
formik.setFieldValue('selectCommand', table.tableName)
|
||||||
|
|
@ -283,10 +310,19 @@ const WizardStep2 = ({
|
||||||
variant="solid"
|
variant="solid"
|
||||||
icon={<FaPlus />}
|
icon={<FaPlus />}
|
||||||
disabled={!values.dataSourceCode}
|
disabled={!values.dataSourceCode}
|
||||||
onClick={() => setShowTableDesignerDialog(true)}
|
onClick={handleNewTable}
|
||||||
>
|
>
|
||||||
New Table
|
New Table
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="default"
|
||||||
|
icon={<FaTable />}
|
||||||
|
disabled={!values.dataSourceCode || !selectedTable}
|
||||||
|
onClick={handleDesignTable}
|
||||||
|
>
|
||||||
|
Design Table
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
@ -832,9 +868,12 @@ const WizardStep2 = ({
|
||||||
|
|
||||||
<SqlTableDesignerDialog
|
<SqlTableDesignerDialog
|
||||||
isOpen={showTableDesignerDialog}
|
isOpen={showTableDesignerDialog}
|
||||||
onClose={() => setShowTableDesignerDialog(false)}
|
onClose={() => {
|
||||||
|
setShowTableDesignerDialog(false)
|
||||||
|
setDesignTableData(null)
|
||||||
|
}}
|
||||||
dataSource={values.dataSourceCode}
|
dataSource={values.dataSourceCode}
|
||||||
initialTableData={null}
|
initialTableData={designTableData}
|
||||||
onDeployed={handleTableDeployed}
|
onDeployed={handleTableDeployed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -196,34 +196,56 @@ function WizardStep6({
|
||||||
|
|
||||||
const resetDemo = () => {
|
const resetDemo = () => {
|
||||||
const startId = uniqueCriteriaId([])
|
const startId = uniqueCriteriaId([])
|
||||||
const approvalId = uniqueCriteriaId([], [startId])
|
const approval1Id = uniqueCriteriaId([], [startId])
|
||||||
const endId = uniqueCriteriaId([], [startId, approvalId])
|
const approval2Id = uniqueCriteriaId([], [startId, approval1Id])
|
||||||
|
const informId = uniqueCriteriaId([], [startId, approval1Id, approval2Id])
|
||||||
|
const endId = uniqueCriteriaId([], [startId, approval1Id, approval2Id, informId])
|
||||||
updateCriteria([
|
updateCriteria([
|
||||||
{
|
{
|
||||||
...normalizeCriteria(emptyCriteria('Start', listFormCode)),
|
...normalizeCriteria(emptyCriteria('Start', listFormCode)),
|
||||||
id: startId,
|
id: startId,
|
||||||
nodeId: startId,
|
nodeId: startId,
|
||||||
title: uniqueCriteriaTitle('Start', []),
|
title: 'İş Akışı Başlat1',
|
||||||
nextOnStart: approvalId,
|
nextOnStart: approval1Id,
|
||||||
positionX: 72,
|
positionX: 34,
|
||||||
positionY: 160,
|
positionY: 28,
|
||||||
} as WorkflowCriteriaDto,
|
} as WorkflowCriteriaDto,
|
||||||
{
|
{
|
||||||
...normalizeCriteria(emptyCriteria('Approval', listFormCode)),
|
...normalizeCriteria(emptyCriteria('Approval', listFormCode)),
|
||||||
id: approvalId,
|
id: approval1Id,
|
||||||
nodeId: approvalId,
|
nodeId: approval1Id,
|
||||||
title: uniqueCriteriaTitle('Approval', []),
|
title: 'Onay1',
|
||||||
nextOnApprove: endId,
|
nextOnApprove: approval2Id,
|
||||||
positionX: 360,
|
nextOnReject: informId,
|
||||||
positionY: 160,
|
positionX: 323,
|
||||||
|
positionY: 28,
|
||||||
|
} as WorkflowCriteriaDto,
|
||||||
|
{
|
||||||
|
...normalizeCriteria(emptyCriteria('Approval', listFormCode)),
|
||||||
|
id: approval2Id,
|
||||||
|
nodeId: approval2Id,
|
||||||
|
title: 'Onay2',
|
||||||
|
nextOnApprove: informId,
|
||||||
|
nextOnReject: informId,
|
||||||
|
positionX: 586,
|
||||||
|
positionY: 28,
|
||||||
|
} as WorkflowCriteriaDto,
|
||||||
|
{
|
||||||
|
...normalizeCriteria(emptyCriteria('Inform', listFormCode)),
|
||||||
|
id: informId,
|
||||||
|
nodeId: informId,
|
||||||
|
title: 'Bilgilendirme1',
|
||||||
|
nextOnStart: endId,
|
||||||
|
positionX: 458,
|
||||||
|
positionY: 336,
|
||||||
} as WorkflowCriteriaDto,
|
} as WorkflowCriteriaDto,
|
||||||
{
|
{
|
||||||
...normalizeCriteria(emptyCriteria('End', listFormCode)),
|
...normalizeCriteria(emptyCriteria('End', listFormCode)),
|
||||||
id: endId,
|
id: endId,
|
||||||
nodeId: endId,
|
nodeId: endId,
|
||||||
title: uniqueCriteriaTitle('End', []),
|
title: 'İş Akışı Bitir1',
|
||||||
positionX: 650,
|
positionX: 792,
|
||||||
positionY: 160,
|
positionY: 336,
|
||||||
} as WorkflowCriteriaDto,
|
} as WorkflowCriteriaDto,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
|
import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui'
|
||||||
import { useDialogContext } from '@/components/ui/Dialog/Dialog'
|
|
||||||
import {
|
import {
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaTrash,
|
FaTrash,
|
||||||
|
|
@ -835,9 +834,8 @@ const STEPS = ['Sütun Tasarımı', 'Entity Ayarları', 'Index / Key', 'İlişki
|
||||||
type Step = 0 | 1 | 2 | 3 | 4
|
type Step = 0 | 1 | 2 | 3 | 4
|
||||||
|
|
||||||
function StepContentWrapper({ children }: { children: React.ReactNode }) {
|
function StepContentWrapper({ children }: { children: React.ReactNode }) {
|
||||||
const { isMaximized } = useDialogContext()
|
|
||||||
return (
|
return (
|
||||||
<div className={isMaximized ? 'flex-1 min-h-0 overflow-auto' : 'min-h-[420px]'}>
|
<div className="min-h-[420px] flex flex-col">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -1810,7 +1808,7 @@ const SqlTableDesignerDialog = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 max-h-80 overflow-y-auto pr-1">
|
<div className="flex flex-col gap-1 pr-1">
|
||||||
{columns.map((col, idx) => {
|
{columns.map((col, idx) => {
|
||||||
const isDuplicate =
|
const isDuplicate =
|
||||||
col.columnName.trim() !== '' &&
|
col.columnName.trim() !== '' &&
|
||||||
|
|
@ -2741,7 +2739,7 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={1100}>
|
<Dialog isOpen={isOpen} onClose={handleClose} onRequestClose={handleClose} width={1100}>
|
||||||
<Dialog.Body className="flex flex-col gap-2">
|
<Dialog.Header className="flex flex-col gap-2">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center gap-3 border-b pb-3 flex-shrink-0">
|
<div className="flex items-center gap-3 border-b pb-3 flex-shrink-0">
|
||||||
<FaTable className="text-2xl text-blue-500" />
|
<FaTable className="text-2xl text-blue-500" />
|
||||||
|
|
@ -2761,7 +2759,9 @@ const SqlTableDesignerDialog = ({
|
||||||
|
|
||||||
{/* Steps */}
|
{/* Steps */}
|
||||||
<div className="flex-shrink-0">{renderStepIndicator()}</div>
|
<div className="flex-shrink-0">{renderStepIndicator()}</div>
|
||||||
|
</Dialog.Header>
|
||||||
|
|
||||||
|
<Dialog.Body className="flex flex-col gap-2">
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<StepContentWrapper>
|
<StepContentWrapper>
|
||||||
{step === 0 && renderColumnDesigner()}
|
{step === 0 && renderColumnDesigner()}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,9 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
Boolean(
|
||||||
|
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||||
|
)
|
||||||
|
|
||||||
const FormDevExpress = (props: {
|
const FormDevExpress = (props: {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -158,9 +160,15 @@ const FormDevExpress = (props: {
|
||||||
const formItemsRef = useRef(formItems)
|
const formItemsRef = useRef(formItems)
|
||||||
const formInstanceRef = useRef<any>()
|
const formInstanceRef = useRef<any>()
|
||||||
const lastContentReadyScriptKeyRef = useRef<string>()
|
const lastContentReadyScriptKeyRef = useRef<string>()
|
||||||
|
const didAutoFocusRef = useRef(false)
|
||||||
const [runtimeReadOnlyFields, setRuntimeReadOnlyFields] = useState<Record<string, boolean>>({})
|
const [runtimeReadOnlyFields, setRuntimeReadOnlyFields] = useState<Record<string, boolean>>({})
|
||||||
const runtimeReadOnlyFieldsRef = useRef<Record<string, boolean>>({})
|
const runtimeReadOnlyFieldsRef = useRef<Record<string, boolean>>({})
|
||||||
|
|
||||||
|
const isTouchLikeDevice = () =>
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.matchMedia?.('(pointer: coarse)').matches ||
|
||||||
|
window.matchMedia?.('(hover: none)').matches)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
formDataRef.current = formData
|
formDataRef.current = formData
|
||||||
}, [formData])
|
}, [formData])
|
||||||
|
|
@ -173,6 +181,10 @@ const FormDevExpress = (props: {
|
||||||
runtimeReadOnlyFieldsRef.current = runtimeReadOnlyFields
|
runtimeReadOnlyFieldsRef.current = runtimeReadOnlyFields
|
||||||
}, [runtimeReadOnlyFields])
|
}, [runtimeReadOnlyFields])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
didAutoFocusRef.current = false
|
||||||
|
}, [listFormCode, mode])
|
||||||
|
|
||||||
const setRuntimeEditorReadOnly = (field: string, readOnly: boolean) => {
|
const setRuntimeEditorReadOnly = (field: string, readOnly: boolean) => {
|
||||||
const resolvedField = findFormFieldKey(formItemsRef.current, field)
|
const resolvedField = findFormFieldKey(formItemsRef.current, field)
|
||||||
const key = String(resolvedField || field || '').toLowerCase()
|
const key = String(resolvedField || field || '').toLowerCase()
|
||||||
|
|
@ -202,11 +214,7 @@ const FormDevExpress = (props: {
|
||||||
setTimeout(() => setFormEditorReadOnly(formInstanceRef.current ?? form, field, readOnly), 0)
|
setTimeout(() => setFormEditorReadOnly(formInstanceRef.current ?? form, field, readOnly), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const runEditorScript = (
|
const runEditorScript = (formItem: SimpleItemWithColData, eventValue: any, component?: any) => {
|
||||||
formItem: SimpleItemWithColData,
|
|
||||||
eventValue: any,
|
|
||||||
component?: any,
|
|
||||||
) => {
|
|
||||||
if (!formItem?.editorScript) {
|
if (!formItem?.editorScript) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +258,9 @@ const FormDevExpress = (props: {
|
||||||
const prevOnValueChanged = formItem.editorOptions?.onValueChanged
|
const prevOnValueChanged = formItem.editorOptions?.onValueChanged
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(index !== undefined && mode !== 'view' ? { autoFocus: index === 1 } : {}),
|
...(index !== undefined && mode !== 'view' && !isTouchLikeDevice()
|
||||||
|
? { autoFocus: index === 1 }
|
||||||
|
: {}),
|
||||||
...(formItem.editorType === 'dxDateBox'
|
...(formItem.editorType === 'dxDateBox'
|
||||||
? {
|
? {
|
||||||
useMaskBehavior: true,
|
useMaskBehavior: true,
|
||||||
|
|
@ -466,7 +476,6 @@ const FormDevExpress = (props: {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateCascadeDisabledStates()
|
updateCascadeDisabledStates()
|
||||||
}, 0)
|
}, 0)
|
||||||
|
|
||||||
}}
|
}}
|
||||||
onContentReady={(e) => {
|
onContentReady={(e) => {
|
||||||
formInstanceRef.current = e.component
|
formInstanceRef.current = e.component
|
||||||
|
|
@ -478,7 +487,8 @@ const FormDevExpress = (props: {
|
||||||
const groupItems = e.component.option('items') as any[]
|
const groupItems = e.component.option('items') as any[]
|
||||||
const firstItem = groupItems?.[0]?.items?.[0]
|
const firstItem = groupItems?.[0]?.items?.[0]
|
||||||
|
|
||||||
if (firstItem?.dataField) {
|
if (!didAutoFocusRef.current && firstItem?.dataField && !isTouchLikeDevice()) {
|
||||||
|
didAutoFocusRef.current = true
|
||||||
const editor = e.component.getEditor(firstItem.dataField)
|
const editor = e.component.getEditor(firstItem.dataField)
|
||||||
mode !== 'view' && editor?.focus()
|
mode !== 'view' && editor?.focus()
|
||||||
}
|
}
|
||||||
|
|
@ -492,98 +502,100 @@ const FormDevExpress = (props: {
|
||||||
colSpan={formGroupItem.colSpan}
|
colSpan={formGroupItem.colSpan}
|
||||||
caption={formGroupItem.caption}
|
caption={formGroupItem.caption}
|
||||||
>
|
>
|
||||||
{(formGroupItem.items as SimpleItemWithColData[])?.filter((formItem) => {
|
{(formGroupItem.items as SimpleItemWithColData[])
|
||||||
if (mode === 'edit') return formItem.allowEditing !== false
|
?.filter((formItem) => {
|
||||||
if (mode === 'new') return formItem.allowAdding !== false
|
if (mode === 'edit') return formItem.allowEditing !== false
|
||||||
return true
|
if (mode === 'new') return formItem.allowAdding !== false
|
||||||
}).map((formItem, i) => {
|
return true
|
||||||
return formItem.editorType2 === PlatformEditorTypes.dxTagBox ? (
|
})
|
||||||
<SimpleItemDx
|
.map((formItem, i) => {
|
||||||
cssClass="font-semibold"
|
return formItem.editorType2 === PlatformEditorTypes.dxTagBox ? (
|
||||||
key={getFormItemKey(formItem, i)}
|
<SimpleItemDx
|
||||||
{...formItem}
|
cssClass="font-semibold"
|
||||||
render={() => (
|
key={getFormItemKey(formItem, i)}
|
||||||
<TagBoxEditorComponent
|
{...formItem}
|
||||||
value={formData[formItem.dataField!] || []}
|
render={() => (
|
||||||
setDefaultValue={false}
|
<TagBoxEditorComponent
|
||||||
values={formData}
|
value={formData[formItem.dataField!] || []}
|
||||||
options={formItem.tagBoxOptions}
|
setDefaultValue={false}
|
||||||
col={formItem.colData}
|
values={formData}
|
||||||
onValueChanged={(e: any) => {
|
options={formItem.tagBoxOptions}
|
||||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
col={formItem.colData}
|
||||||
formDataRef.current = newData
|
onValueChanged={(e: any) => {
|
||||||
setFormData(newData)
|
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||||
runEditorScript(formItem, e, formInstanceRef.current)
|
formDataRef.current = newData
|
||||||
}}
|
setFormData(newData)
|
||||||
editorOptions={getEditorOptions(formItem)}
|
runEditorScript(formItem, e, formInstanceRef.current)
|
||||||
></TagBoxEditorComponent>
|
}}
|
||||||
)}
|
editorOptions={getEditorOptions(formItem)}
|
||||||
label={{
|
></TagBoxEditorComponent>
|
||||||
text: translate('::' + formItem.colData?.captionName),
|
)}
|
||||||
className: 'font-semibold',
|
label={{
|
||||||
}}
|
text: translate('::' + formItem.colData?.captionName),
|
||||||
></SimpleItemDx>
|
className: 'font-semibold',
|
||||||
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
}}
|
||||||
<SimpleItemDx
|
></SimpleItemDx>
|
||||||
cssClass="font-semibold"
|
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
||||||
key={getFormItemKey(formItem, i)}
|
<SimpleItemDx
|
||||||
{...formItem}
|
cssClass="font-semibold"
|
||||||
render={() => (
|
key={getFormItemKey(formItem, i)}
|
||||||
<GridBoxEditorComponent
|
{...formItem}
|
||||||
value={formData[formItem.dataField!] || []}
|
render={() => (
|
||||||
values={formData}
|
<GridBoxEditorComponent
|
||||||
options={formItem.gridBoxOptions}
|
value={formData[formItem.dataField!] || []}
|
||||||
col={formItem.colData}
|
values={formData}
|
||||||
onValueChanged={(e: any) => {
|
options={formItem.gridBoxOptions}
|
||||||
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
col={formItem.colData}
|
||||||
formDataRef.current = newData
|
onValueChanged={(e: any) => {
|
||||||
setFormData(newData)
|
const newData = { ...formDataRef.current, [formItem.dataField!]: e }
|
||||||
runEditorScript(formItem, e, formInstanceRef.current)
|
formDataRef.current = newData
|
||||||
}}
|
setFormData(newData)
|
||||||
editorOptions={getEditorOptions(formItem)}
|
runEditorScript(formItem, e, formInstanceRef.current)
|
||||||
></GridBoxEditorComponent>
|
}}
|
||||||
)}
|
editorOptions={getEditorOptions(formItem)}
|
||||||
label={{
|
></GridBoxEditorComponent>
|
||||||
text: translate('::' + formItem.colData?.captionName),
|
)}
|
||||||
className: 'font-semibold',
|
label={{
|
||||||
}}
|
text: translate('::' + formItem.colData?.captionName),
|
||||||
></SimpleItemDx>
|
className: 'font-semibold',
|
||||||
) : formItem.editorType2 === PlatformEditorTypes.dxImageUpload ? (
|
}}
|
||||||
<SimpleItemDx
|
></SimpleItemDx>
|
||||||
cssClass="font-semibold"
|
) : formItem.editorType2 === PlatformEditorTypes.dxImageUpload ? (
|
||||||
key={getFormItemKey(formItem, i)}
|
<SimpleItemDx
|
||||||
dataField={formItem.dataField}
|
cssClass="font-semibold"
|
||||||
name={formItem.name}
|
key={getFormItemKey(formItem, i)}
|
||||||
colSpan={formItem.colSpan}
|
dataField={formItem.dataField}
|
||||||
isRequired={formItem.isRequired}
|
name={formItem.name}
|
||||||
render={() => (
|
colSpan={formItem.colSpan}
|
||||||
<ImageUploadEditorComponent
|
isRequired={formItem.isRequired}
|
||||||
value={formData[formItem.dataField!]}
|
render={() => (
|
||||||
options={formItem.imageUploadOptions}
|
<ImageUploadEditorComponent
|
||||||
onValueChanged={(val: any) => {
|
value={formData[formItem.dataField!]}
|
||||||
const newData = { ...formDataRef.current, [formItem.dataField!]: val }
|
options={formItem.imageUploadOptions}
|
||||||
formDataRef.current = newData
|
onValueChanged={(val: any) => {
|
||||||
setFormData(newData)
|
const newData = { ...formDataRef.current, [formItem.dataField!]: val }
|
||||||
runEditorScript(formItem, val, formInstanceRef.current)
|
formDataRef.current = newData
|
||||||
}}
|
setFormData(newData)
|
||||||
editorOptions={getEditorOptions(formItem)}
|
runEditorScript(formItem, val, formInstanceRef.current)
|
||||||
/>
|
}}
|
||||||
)}
|
editorOptions={getEditorOptions(formItem)}
|
||||||
label={{
|
/>
|
||||||
text: translate('::' + formItem.colData?.captionName),
|
)}
|
||||||
className: 'font-semibold',
|
label={{
|
||||||
}}
|
text: translate('::' + formItem.colData?.captionName),
|
||||||
></SimpleItemDx>
|
className: 'font-semibold',
|
||||||
) : (
|
}}
|
||||||
<SimpleItemDx
|
></SimpleItemDx>
|
||||||
cssClass="font-semibold"
|
) : (
|
||||||
key={getFormItemKey(formItem, i)}
|
<SimpleItemDx
|
||||||
{...formItem}
|
cssClass="font-semibold"
|
||||||
editorOptions={getEditorOptions(formItem, i)}
|
key={getFormItemKey(formItem, i)}
|
||||||
label={{ text: translate('::' + formItem.colData?.captionName) }}
|
{...formItem}
|
||||||
/>
|
editorOptions={getEditorOptions(formItem, i)}
|
||||||
)
|
label={{ text: translate('::' + formItem.colData?.captionName) }}
|
||||||
})}
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</GroupItemDx>
|
</GroupItemDx>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,13 @@ import { layoutTypes } from '../admin/listForm/edit/types'
|
||||||
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
||||||
import { useListFormColumns } from '../list/useListFormColumns'
|
import { useListFormColumns } from '../list/useListFormColumns'
|
||||||
|
|
||||||
|
const flattenFormItems = (items: any[] = []): SimpleItemWithColData[] =>
|
||||||
|
items.flatMap((item) => [
|
||||||
|
...(item?.dataField ? [item] : []),
|
||||||
|
...flattenFormItems(item?.items || []),
|
||||||
|
...(item?.tabs || []).flatMap((tab: any) => flattenFormItems(tab?.items || [])),
|
||||||
|
])
|
||||||
|
|
||||||
const useGridData = (props: {
|
const useGridData = (props: {
|
||||||
mode: RowMode
|
mode: RowMode
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -41,6 +48,7 @@ const useGridData = (props: {
|
||||||
const [permissionResults, setPermissionResults] = useState<PermissionResults>()
|
const [permissionResults, setPermissionResults] = useState<PermissionResults>()
|
||||||
|
|
||||||
const refForm = useRef<FormRef>(null)
|
const refForm = useRef<FormRef>(null)
|
||||||
|
const previousFormDataRef = useRef<any>()
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -306,18 +314,36 @@ const useGridData = (props: {
|
||||||
setGridReady(true)
|
setGridReady(true)
|
||||||
}, [gridDto])
|
}, [gridDto])
|
||||||
|
|
||||||
// formData değiştiğinde sadece lookup datasource'ları güncelle
|
// formData değiştiğinde sadece etkilenen cascading lookup datasource'ları güncelle
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gridDto || !formItems.length) {
|
if (!gridDto || !formItems.length) {
|
||||||
|
previousFormDataRef.current = formData
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// View mode'da formData olsa da olmasa da cascading alanlar için dataSource oluşturulmalı
|
const previousFormData = previousFormDataRef.current
|
||||||
const updatedItems = formItems.map((groupItem) => ({
|
const changedFields = previousFormData
|
||||||
...groupItem,
|
? Object.keys({ ...(previousFormData || {}), ...(formData || {}) }).filter(
|
||||||
items: (groupItem.items as SimpleItemWithColData[])?.map((item) => {
|
(field) => !Object.is(previousFormData?.[field], formData?.[field]),
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const shouldRefreshLookup = (item: SimpleItemWithColData) => {
|
||||||
|
const cascadeParentFields = item.colData?.lookupDto?.cascadeParentFields
|
||||||
|
?.split(',')
|
||||||
|
.map((field: string) => field.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
return (
|
||||||
|
!previousFormData ||
|
||||||
|
cascadeParentFields?.some((field: string) => changedFields.includes(field))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateItems = (items: any[] = []) =>
|
||||||
|
items.map((item) => {
|
||||||
const colData = gridDto.columnFormats.find((x) => x.fieldName === item.dataField)
|
const colData = gridDto.columnFormats.find((x) => x.fieldName === item.dataField)
|
||||||
if (colData?.lookupDto?.dataSourceType) {
|
if (colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item)) {
|
||||||
const currentDataSource = item.editorOptions?.dataSource
|
const currentDataSource = item.editorOptions?.dataSource
|
||||||
const keepCustomDataSource =
|
const keepCustomDataSource =
|
||||||
currentDataSource !== undefined && typeof currentDataSource?.load !== 'function'
|
currentDataSource !== undefined && typeof currentDataSource?.load !== 'function'
|
||||||
|
|
@ -330,16 +356,55 @@ const useGridData = (props: {
|
||||||
dataSource: keepCustomDataSource
|
dataSource: keepCustomDataSource
|
||||||
? currentDataSource
|
? currentDataSource
|
||||||
: getLookupDataSource(colData?.editorOptions, colData, formData || null),
|
: getLookupDataSource(colData?.editorOptions, colData, formData || null),
|
||||||
valueExpr: item.editorOptions?.valueExpr ?? colData?.lookupDto?.valueExpr?.toLowerCase(),
|
valueExpr:
|
||||||
|
item.editorOptions?.valueExpr ?? colData?.lookupDto?.valueExpr?.toLowerCase(),
|
||||||
displayExpr:
|
displayExpr:
|
||||||
item.editorOptions?.displayExpr ?? colData?.lookupDto?.displayExpr?.toLowerCase(),
|
item.editorOptions?.displayExpr ?? colData?.lookupDto?.displayExpr?.toLowerCase(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item?.items?.length) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
items: updateItems(item.items),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item?.tabs?.length) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
tabs: item.tabs.map((tab: any) => ({
|
||||||
|
...tab,
|
||||||
|
items: updateItems(tab.items),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
}),
|
})
|
||||||
|
|
||||||
|
const hasAffectedLookup =
|
||||||
|
!previousFormData ||
|
||||||
|
formItems
|
||||||
|
.flatMap((group) => flattenFormItems([group]))
|
||||||
|
.some((item) => item.colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item))
|
||||||
|
|
||||||
|
if (!hasAffectedLookup) {
|
||||||
|
previousFormDataRef.current = formData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedItems = formItems.map((groupItem) => ({
|
||||||
|
...groupItem,
|
||||||
|
items: updateItems(groupItem.items as any[]),
|
||||||
|
tabs: (groupItem as any).tabs?.map((tab: any) => ({
|
||||||
|
...tab,
|
||||||
|
items: updateItems(tab.items),
|
||||||
|
})),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
previousFormDataRef.current = formData
|
||||||
setFormItems(updatedItems)
|
setFormItems(updatedItems)
|
||||||
}, [formData, gridDto])
|
}, [formData, gridDto])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,19 +239,28 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
Boolean(
|
||||||
|
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||||
|
)
|
||||||
|
|
||||||
|
const isTouchLikeDevice = () =>
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.matchMedia?.('(pointer: coarse)').matches ||
|
||||||
|
window.matchMedia?.('(hover: none)').matches)
|
||||||
|
|
||||||
const Grid = (props: GridProps) => {
|
const Grid = (props: GridProps) => {
|
||||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { smaller } = useResponsive()
|
const { smaller } = useResponsive()
|
||||||
const currentUser = useStoreState((state) => state.auth.user)
|
const currentUser = useStoreState((state) => state.auth.user)
|
||||||
|
const useMobileEditPopup = smaller.md || isTouchLikeDevice()
|
||||||
|
|
||||||
const gridRef = useRef<DataGridRef>()
|
const gridRef = useRef<DataGridRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||||
const editingFormInstanceRef = useRef<any>()
|
const editingFormInstanceRef = useRef<any>()
|
||||||
|
const lastEditingContentReadyScriptKeyRef = useRef<string>()
|
||||||
// Edit popup state kaydetmeyi engellemek için flag
|
// Edit popup state kaydetmeyi engellemek için flag
|
||||||
const isEditingRef = useRef(false)
|
const isEditingRef = useRef(false)
|
||||||
|
|
||||||
|
|
@ -728,6 +737,12 @@ const Grid = (props: GridProps) => {
|
||||||
const onEditorPreparing = useCallback(
|
const onEditorPreparing = useCallback(
|
||||||
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
|
(editor: DataGridTypes.EditorPreparingEvent<any, any>) => {
|
||||||
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
||||||
|
if (isTouchLikeDevice()) {
|
||||||
|
editor.editorOptions.autoFocus = false
|
||||||
|
editor.editorOptions.focusStateEnabled = false
|
||||||
|
editor.editorOptions.selectTextOnFocus = false
|
||||||
|
}
|
||||||
|
|
||||||
const formItem = gridDto.gridOptions.editingFormDto
|
const formItem = gridDto.gridOptions.editingFormDto
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
.flatMap((group) => flattenEditingFormItems([group]))
|
||||||
.find((i) => i.dataField === editor.dataField)
|
.find((i) => i.dataField === editor.dataField)
|
||||||
|
|
@ -1181,20 +1196,20 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||||
Object.assign(defaultEditorOptions, {
|
Object.assign(defaultEditorOptions, {
|
||||||
type: 'date',
|
type: 'date',
|
||||||
dateSerializationFormat: 'yyyy-MM-dd',
|
dateSerializationFormat: 'yyyy-MM-dd',
|
||||||
displayFormat: 'shortDate',
|
displayFormat: 'shortDate',
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
||||||
) {
|
) {
|
||||||
Object.assign(defaultEditorOptions, {
|
Object.assign(defaultEditorOptions, {
|
||||||
type: 'datetime',
|
type: 'datetime',
|
||||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||||
displayFormat: 'shortDateShortTime',
|
displayFormat: 'shortDateShortTime',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Her item'a placeholder olarak captionName ekle
|
// Her item'a placeholder olarak captionName ekle
|
||||||
|
|
@ -1217,6 +1232,15 @@ const Grid = (props: GridProps) => {
|
||||||
...forcedEditorOptions,
|
...forcedEditorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useMobileEditPopup) {
|
||||||
|
editorOptions = {
|
||||||
|
...editorOptions,
|
||||||
|
autoFocus: false,
|
||||||
|
focusStateEnabled: false,
|
||||||
|
selectTextOnFocus: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (editorOptions?.buttons) {
|
if (editorOptions?.buttons) {
|
||||||
editorOptions.buttons = (editorOptions?.buttons || []).map((btn: any) => {
|
editorOptions.buttons = (editorOptions?.buttons || []).map((btn: any) => {
|
||||||
if (btn?.options?.onClick && typeof btn.options.onClick === 'string') {
|
if (btn?.options?.onClick && typeof btn.options.onClick === 'string') {
|
||||||
|
|
@ -1261,7 +1285,7 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
return item
|
return item
|
||||||
},
|
},
|
||||||
[gridDto, mode, searchParams, extraFilters],
|
[gridDto, mode, searchParams, extraFilters, useMobileEditPopup],
|
||||||
)
|
)
|
||||||
|
|
||||||
// WidgetGroup yüksekliğini hesapla
|
// WidgetGroup yüksekliğini hesapla
|
||||||
|
|
@ -1514,7 +1538,7 @@ const Grid = (props: GridProps) => {
|
||||||
/>
|
/>
|
||||||
<Editing
|
<Editing
|
||||||
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
||||||
mode={smaller.md ? 'form' : gridDto.gridOptions.editingOptionDto?.mode}
|
mode={gridDto.gridOptions.editingOptionDto?.mode}
|
||||||
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
||||||
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
||||||
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
||||||
|
|
@ -1534,10 +1558,18 @@ const Grid = (props: GridProps) => {
|
||||||
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
||||||
hideOnOutsideClick:
|
hideOnOutsideClick:
|
||||||
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
||||||
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
width: useMobileEditPopup
|
||||||
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
? '100%'
|
||||||
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||||
fullScreen: isPopupFullScreen,
|
height: useMobileEditPopup
|
||||||
|
? '100dvh'
|
||||||
|
: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||||
|
resizeEnabled:
|
||||||
|
!useMobileEditPopup &&
|
||||||
|
gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||||
|
fullScreen: useMobileEditPopup || isPopupFullScreen,
|
||||||
|
dragEnabled: !useMobileEditPopup,
|
||||||
|
restorePosition: !useMobileEditPopup,
|
||||||
toolbarItems: [
|
toolbarItems: [
|
||||||
{
|
{
|
||||||
widget: 'dxButton',
|
widget: 'dxButton',
|
||||||
|
|
@ -1581,6 +1613,7 @@ const Grid = (props: GridProps) => {
|
||||||
}}
|
}}
|
||||||
form={{
|
form={{
|
||||||
colCount: 1,
|
colCount: 1,
|
||||||
|
focusStateEnabled: !useMobileEditPopup,
|
||||||
onContentReady: (e) => {
|
onContentReady: (e) => {
|
||||||
editingFormInstanceRef.current = e.component
|
editingFormInstanceRef.current = e.component
|
||||||
|
|
||||||
|
|
@ -1601,15 +1634,27 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const runReadOnlyScripts = () => {
|
const runReadOnlyScripts = () => {
|
||||||
const editorValues = gridDto.gridOptions.editingFormDto
|
const formItems = gridDto.gridOptions.editingFormDto.flatMap((group) =>
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
flattenEditingFormItems([group]),
|
||||||
.reduce<Record<string, any>>((values, formItem) => {
|
)
|
||||||
|
const scriptItems = formItems.filter((formItem) =>
|
||||||
|
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!scriptItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const editorValues = formItems.reduce<Record<string, any>>(
|
||||||
|
(values, formItem) => {
|
||||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||||
if (editorInstance?.option) {
|
if (editorInstance?.option) {
|
||||||
values[formItem.dataField] = editorInstance.option('value')
|
values[formItem.dataField] = editorInstance.option('value')
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}, {})
|
},
|
||||||
|
{},
|
||||||
|
)
|
||||||
const formData = {
|
const formData = {
|
||||||
...editingFormDataRef.current,
|
...editingFormDataRef.current,
|
||||||
...(form?.option?.('formData') || {}),
|
...(form?.option?.('formData') || {}),
|
||||||
|
|
@ -1617,38 +1662,47 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
editingFormDataRef.current = { ...formData }
|
editingFormDataRef.current = { ...formData }
|
||||||
|
|
||||||
gridDto.gridOptions.editingFormDto
|
const scriptKey = `${mode}|${String(rowKey)}|${scriptItems
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
.map((formItem) => formItem.dataField)
|
||||||
.filter((formItem) =>
|
.join('|')}|${JSON.stringify(formData)}`
|
||||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
|
||||||
)
|
|
||||||
.forEach((formItem) => {
|
|
||||||
try {
|
|
||||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
|
||||||
const editorValue =
|
|
||||||
editorInstance?.option?.('value') ??
|
|
||||||
getValueByField(formData, formItem.dataField)
|
|
||||||
const editor = {
|
|
||||||
dataField: formItem.dataField,
|
|
||||||
component: grid,
|
|
||||||
}
|
|
||||||
const e = {
|
|
||||||
component: form,
|
|
||||||
dataField: formItem.dataField,
|
|
||||||
value: editorValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
executeEditorScript(formItem.editorScript!, {
|
if (lastEditingContentReadyScriptKeyRef.current === scriptKey) {
|
||||||
formData,
|
return
|
||||||
e,
|
}
|
||||||
editor,
|
|
||||||
runtimeSetEditorReadOnly,
|
lastEditingContentReadyScriptKeyRef.current = scriptKey
|
||||||
setFormData,
|
|
||||||
})
|
scriptItems.forEach((formItem) => {
|
||||||
} catch (err) {
|
try {
|
||||||
console.error('Script exec error on contentReady', formItem.dataField, err)
|
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||||
|
const editorValue =
|
||||||
|
editorInstance?.option?.('value') ??
|
||||||
|
getValueByField(formData, formItem.dataField)
|
||||||
|
const editor = {
|
||||||
|
dataField: formItem.dataField,
|
||||||
|
component: grid,
|
||||||
}
|
}
|
||||||
})
|
const e = {
|
||||||
|
component: form,
|
||||||
|
dataField: formItem.dataField,
|
||||||
|
value: editorValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
executeEditorScript(formItem.editorScript!, {
|
||||||
|
formData,
|
||||||
|
e,
|
||||||
|
editor,
|
||||||
|
runtimeSetEditorReadOnly,
|
||||||
|
setFormData,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Script exec error on contentReady',
|
||||||
|
formItem.dataField,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
runReadOnlyScripts()
|
runReadOnlyScripts()
|
||||||
|
|
@ -1704,7 +1758,6 @@ const Grid = (props: GridProps) => {
|
||||||
console.error('Script exec error', err)
|
console.error('Script exec error', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
items:
|
items:
|
||||||
|
|
|
||||||
|
|
@ -227,19 +227,28 @@ const getValueByField = (data: Record<string, any> = {}, field?: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
const shouldRunEditorScriptOnContentReady = (script?: string) =>
|
||||||
Boolean(script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')))
|
Boolean(
|
||||||
|
script && (script.includes('setEditorReadOnly') || script.includes('runtimeSetEditorReadOnly')),
|
||||||
|
)
|
||||||
|
|
||||||
|
const isTouchLikeDevice = () =>
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.matchMedia?.('(pointer: coarse)').matches ||
|
||||||
|
window.matchMedia?.('(hover: none)').matches)
|
||||||
|
|
||||||
const Tree = (props: TreeProps) => {
|
const Tree = (props: TreeProps) => {
|
||||||
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { smaller } = useResponsive()
|
const { smaller } = useResponsive()
|
||||||
const currentUser = useStoreState((state) => state.auth.user)
|
const currentUser = useStoreState((state) => state.auth.user)
|
||||||
|
const useMobileEditPopup = smaller.md || isTouchLikeDevice()
|
||||||
|
|
||||||
const gridRef = useRef<TreeListRef>()
|
const gridRef = useRef<TreeListRef>()
|
||||||
const refListFormCode = useRef('')
|
const refListFormCode = useRef('')
|
||||||
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
const widgetGroupRef = useRef<HTMLDivElement>(null)
|
||||||
const editingFormDataRef = useRef<Record<string, any>>({})
|
const editingFormDataRef = useRef<Record<string, any>>({})
|
||||||
const editingFormInstanceRef = useRef<any>()
|
const editingFormInstanceRef = useRef<any>()
|
||||||
|
const lastEditingContentReadyScriptKeyRef = useRef<string>()
|
||||||
// Edit popup state kaydetmeyi engellemek için flag
|
// Edit popup state kaydetmeyi engellemek için flag
|
||||||
const isEditingRef = useRef(false)
|
const isEditingRef = useRef(false)
|
||||||
|
|
||||||
|
|
@ -677,6 +686,12 @@ const Tree = (props: TreeProps) => {
|
||||||
|
|
||||||
function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) {
|
function onEditorPreparing(editor: TreeListTypes.EditorPreparingEvent) {
|
||||||
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
if (editor.parentType === 'dataRow' && editor.dataField && gridDto) {
|
||||||
|
if (isTouchLikeDevice()) {
|
||||||
|
editor.editorOptions.autoFocus = false
|
||||||
|
editor.editorOptions.focusStateEnabled = false
|
||||||
|
editor.editorOptions.selectTextOnFocus = false
|
||||||
|
}
|
||||||
|
|
||||||
const formItem = gridDto.gridOptions.editingFormDto
|
const formItem = gridDto.gridOptions.editingFormDto
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
.flatMap((group) => flattenEditingFormItems([group]))
|
||||||
.find((i) => i.dataField === editor.dataField)
|
.find((i) => i.dataField === editor.dataField)
|
||||||
|
|
@ -1180,7 +1195,7 @@ const Tree = (props: TreeProps) => {
|
||||||
<RemoteOperations filtering={true} sorting={true} grouping={false} />
|
<RemoteOperations filtering={true} sorting={true} grouping={false} />
|
||||||
<Editing
|
<Editing
|
||||||
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
refreshMode={gridDto.gridOptions.editingOptionDto?.refreshMode}
|
||||||
mode={smaller.md ? 'form' : gridDto.gridOptions.editingOptionDto?.mode}
|
mode={gridDto.gridOptions.editingOptionDto?.mode}
|
||||||
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting}
|
||||||
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating}
|
||||||
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding}
|
||||||
|
|
@ -1196,10 +1211,18 @@ const Tree = (props: TreeProps) => {
|
||||||
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
showTitle: gridDto.gridOptions.editingOptionDto?.popup?.showTitle,
|
||||||
hideOnOutsideClick:
|
hideOnOutsideClick:
|
||||||
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
gridDto.gridOptions.editingOptionDto?.popup?.hideOnOutsideClick,
|
||||||
width: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
width: useMobileEditPopup
|
||||||
height: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
? '100%'
|
||||||
resizeEnabled: gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
: gridDto.gridOptions.editingOptionDto?.popup?.width,
|
||||||
fullScreen: isPopupFullScreen,
|
height: useMobileEditPopup
|
||||||
|
? '100dvh'
|
||||||
|
: gridDto.gridOptions.editingOptionDto?.popup?.height,
|
||||||
|
resizeEnabled:
|
||||||
|
!useMobileEditPopup &&
|
||||||
|
gridDto.gridOptions.editingOptionDto?.popup?.resizeEnabled,
|
||||||
|
fullScreen: useMobileEditPopup || isPopupFullScreen,
|
||||||
|
dragEnabled: !useMobileEditPopup,
|
||||||
|
restorePosition: !useMobileEditPopup,
|
||||||
toolbarItems: [
|
toolbarItems: [
|
||||||
{
|
{
|
||||||
widget: 'dxButton',
|
widget: 'dxButton',
|
||||||
|
|
@ -1243,6 +1266,7 @@ const Tree = (props: TreeProps) => {
|
||||||
}}
|
}}
|
||||||
form={{
|
form={{
|
||||||
colCount: 1,
|
colCount: 1,
|
||||||
|
focusStateEnabled: !useMobileEditPopup,
|
||||||
onContentReady: (e) => {
|
onContentReady: (e) => {
|
||||||
editingFormInstanceRef.current = e.component
|
editingFormInstanceRef.current = e.component
|
||||||
|
|
||||||
|
|
@ -1263,15 +1287,27 @@ const Tree = (props: TreeProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const runReadOnlyScripts = () => {
|
const runReadOnlyScripts = () => {
|
||||||
const editorValues = gridDto.gridOptions.editingFormDto
|
const formItems = gridDto.gridOptions.editingFormDto.flatMap((group) =>
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
flattenEditingFormItems([group]),
|
||||||
.reduce<Record<string, any>>((values, formItem) => {
|
)
|
||||||
|
const scriptItems = formItems.filter((formItem) =>
|
||||||
|
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!scriptItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const editorValues = formItems.reduce<Record<string, any>>(
|
||||||
|
(values, formItem) => {
|
||||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||||
if (editorInstance?.option) {
|
if (editorInstance?.option) {
|
||||||
values[formItem.dataField] = editorInstance.option('value')
|
values[formItem.dataField] = editorInstance.option('value')
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}, {})
|
},
|
||||||
|
{},
|
||||||
|
)
|
||||||
const formData = {
|
const formData = {
|
||||||
...editingFormDataRef.current,
|
...editingFormDataRef.current,
|
||||||
...(form?.option?.('formData') || {}),
|
...(form?.option?.('formData') || {}),
|
||||||
|
|
@ -1279,38 +1315,47 @@ const Tree = (props: TreeProps) => {
|
||||||
}
|
}
|
||||||
editingFormDataRef.current = { ...formData }
|
editingFormDataRef.current = { ...formData }
|
||||||
|
|
||||||
gridDto.gridOptions.editingFormDto
|
const scriptKey = `${mode}|${String(rowKey)}|${scriptItems
|
||||||
.flatMap((group) => flattenEditingFormItems([group]))
|
.map((formItem) => formItem.dataField)
|
||||||
.filter((formItem) =>
|
.join('|')}|${JSON.stringify(formData)}`
|
||||||
shouldRunEditorScriptOnContentReady(formItem.editorScript),
|
|
||||||
)
|
|
||||||
.forEach((formItem) => {
|
|
||||||
try {
|
|
||||||
const editorInstance = form?.getEditor?.(formItem.dataField)
|
|
||||||
const editorValue =
|
|
||||||
editorInstance?.option?.('value') ??
|
|
||||||
getValueByField(formData, formItem.dataField)
|
|
||||||
const editor = {
|
|
||||||
dataField: formItem.dataField,
|
|
||||||
component: grid,
|
|
||||||
}
|
|
||||||
const e = {
|
|
||||||
component: form,
|
|
||||||
dataField: formItem.dataField,
|
|
||||||
value: editorValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
executeEditorScript(formItem.editorScript!, {
|
if (lastEditingContentReadyScriptKeyRef.current === scriptKey) {
|
||||||
formData,
|
return
|
||||||
e,
|
}
|
||||||
editor,
|
|
||||||
runtimeSetEditorReadOnly,
|
lastEditingContentReadyScriptKeyRef.current = scriptKey
|
||||||
setFormData,
|
|
||||||
})
|
scriptItems.forEach((formItem) => {
|
||||||
} catch (err) {
|
try {
|
||||||
console.error('Script exec error on contentReady', formItem.dataField, err)
|
const editorInstance = form?.getEditor?.(formItem.dataField)
|
||||||
|
const editorValue =
|
||||||
|
editorInstance?.option?.('value') ??
|
||||||
|
getValueByField(formData, formItem.dataField)
|
||||||
|
const editor = {
|
||||||
|
dataField: formItem.dataField,
|
||||||
|
component: grid,
|
||||||
}
|
}
|
||||||
})
|
const e = {
|
||||||
|
component: form,
|
||||||
|
dataField: formItem.dataField,
|
||||||
|
value: editorValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
executeEditorScript(formItem.editorScript!, {
|
||||||
|
formData,
|
||||||
|
e,
|
||||||
|
editor,
|
||||||
|
runtimeSetEditorReadOnly,
|
||||||
|
setFormData,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Script exec error on contentReady',
|
||||||
|
formItem.dataField,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
runReadOnlyScripts()
|
runReadOnlyScripts()
|
||||||
|
|
@ -1366,7 +1411,6 @@ const Tree = (props: TreeProps) => {
|
||||||
console.error('Script exec error', err)
|
console.error('Script exec error', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
items:
|
items:
|
||||||
|
|
@ -1388,7 +1432,9 @@ const Tree = (props: TreeProps) => {
|
||||||
let parsedEditorOptions: EditorOptionsWithButtons = {}
|
let parsedEditorOptions: EditorOptionsWithButtons = {}
|
||||||
const forcedEditorOptions: EditorOptionsWithButtons = {}
|
const forcedEditorOptions: EditorOptionsWithButtons = {}
|
||||||
try {
|
try {
|
||||||
parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {}
|
parsedEditorOptions = i.editorOptions
|
||||||
|
? JSON.parse(i.editorOptions)
|
||||||
|
: {}
|
||||||
|
|
||||||
const rawFilter = searchParams?.get('filter')
|
const rawFilter = searchParams?.get('filter')
|
||||||
if (rawFilter) {
|
if (rawFilter) {
|
||||||
|
|
@ -1420,20 +1466,20 @@ const Tree = (props: TreeProps) => {
|
||||||
|
|
||||||
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
if (listFormField?.sourceDbType === DbTypeEnum.Date) {
|
||||||
Object.assign(defaultEditorOptions, {
|
Object.assign(defaultEditorOptions, {
|
||||||
type: 'date',
|
type: 'date',
|
||||||
dateSerializationFormat: 'yyyy-MM-dd',
|
dateSerializationFormat: 'yyyy-MM-dd',
|
||||||
displayFormat: 'shortDate',
|
displayFormat: 'shortDate',
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime ||
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
listFormField?.sourceDbType === DbTypeEnum.DateTime2 ||
|
||||||
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset
|
||||||
) {
|
) {
|
||||||
Object.assign(defaultEditorOptions, {
|
Object.assign(defaultEditorOptions, {
|
||||||
type: 'datetime',
|
type: 'datetime',
|
||||||
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss',
|
||||||
displayFormat: 'shortDateShortTime',
|
displayFormat: 'shortDateShortTime',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
editorOptions = {
|
editorOptions = {
|
||||||
|
|
@ -1442,6 +1488,15 @@ const Tree = (props: TreeProps) => {
|
||||||
...forcedEditorOptions,
|
...forcedEditorOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useMobileEditPopup) {
|
||||||
|
editorOptions = {
|
||||||
|
...editorOptions,
|
||||||
|
autoFocus: false,
|
||||||
|
focusStateEnabled: false,
|
||||||
|
selectTextOnFocus: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (editorOptions?.buttons) {
|
if (editorOptions?.buttons) {
|
||||||
editorOptions.buttons = (editorOptions?.buttons || []).map(
|
editorOptions.buttons = (editorOptions?.buttons || []).map(
|
||||||
(btn: any) => {
|
(btn: any) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue