Wizard kısmında Tree,Gantt,Scheduler seçenekleri eklendi

This commit is contained in:
Sedat Öztürk 2026-05-03 14:35:52 +03:00
parent b2dfb04879
commit 8dda4498ef
7 changed files with 274 additions and 9 deletions

View file

@ -48,6 +48,25 @@ public class ListFormWizardDto
public string KeyFieldName { get; set; }
public DbType KeyFieldDbSourceType { get; set; }
// Tree options (required when DefaultLayout = "tree")
public string TreeKeyExpr { get; set; }
public string TreeParentIdExpr { get; set; }
public bool TreeAutoExpandAll { get; set; }
// Gantt options (required when DefaultLayout = "gantt")
public string GanttKeyExpr { get; set; }
public string GanttParentIdExpr { get; set; }
public bool GanttAutoExpandAll { get; set; }
public string GanttTitleExpr { get; set; }
public string GanttStartExpr { get; set; }
public string GanttEndExpr { get; set; }
public string GanttProgressExpr { get; set; }
// Scheduler options (required when DefaultLayout = "scheduler")
public string SchedulerTextExpr { get; set; }
public string SchedulerStartDateExpr { get; set; }
public string SchedulerEndDateExpr { get; set; }
public List<WizardColumnGroupInputDto> Groups { get; set; } = new();
}

View file

@ -284,6 +284,34 @@ public class ListFormWizardAppService(
PagerOptionJson = WizardConsts.DefaultPagerOptionJson,
EditingOptionJson = WizardConsts.DefaultEditingOptionJson(titleLangKey, 600, 500, input.AllowDeleting, input.AllowAdding, input.AllowUpdating, input.ConfirmDelete, false, input.AllowDetail),
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
TreeOptionJson = (input.Tree || input.DefaultLayout == "tree") && !string.IsNullOrEmpty(input.TreeParentIdExpr)
? JsonSerializer.Serialize(new TreeOptionDto
{
KeyExpr = input.TreeKeyExpr,
ParentIdExpr = input.TreeParentIdExpr,
AutoExpandAll = input.TreeAutoExpandAll,
})
: null,
GanttOptionJson = (input.Gantt || input.DefaultLayout == "gantt") && !string.IsNullOrEmpty(input.GanttParentIdExpr)
? JsonSerializer.Serialize(new GanttOptionDto
{
KeyExpr = input.GanttKeyExpr,
ParentIdExpr = input.GanttParentIdExpr,
AutoExpandAll = input.GanttAutoExpandAll,
TitleExpr = input.GanttTitleExpr,
StartExpr = input.GanttStartExpr,
EndExpr = input.GanttEndExpr,
ProgressExpr = input.GanttProgressExpr,
})
: null,
SchedulerOptionJson = (input.Scheduler || input.DefaultLayout == "scheduler") && !string.IsNullOrEmpty(input.SchedulerTextExpr)
? JsonSerializer.Serialize(new SchedulerOptionDto
{
TextExpr = input.SchedulerTextExpr,
StartDateExpr = input.SchedulerStartDateExpr,
EndDateExpr = input.SchedulerEndDateExpr,
})
: null,
}, autoSave: true);
// ListFormField - each item in each group becomes a visible field record

View file

@ -11,7 +11,7 @@
"AllowDeleting": true,
"AllowDetail": false,
"ConfirmDelete": true,
"DefaultLayout": "grid",
"DefaultLayout": "tree",
"Grid": true,
"Pivot": true,
"Tree": true,
@ -35,6 +35,19 @@
"SelectCommand": "Adm_T_Department",
"KeyFieldName": "Id",
"KeyFieldDbSourceType": 9,
"TreeKeyExpr": "Id",
"TreeParentIdExpr": "ParentId",
"TreeAutoExpandAll": true,
"GanttKeyExpr": "",
"GanttParentIdExpr": "",
"GanttAutoExpandAll": false,
"GanttTitleExpr": "",
"GanttStartExpr": "",
"GanttEndExpr": "",
"GanttProgressExpr": "",
"SchedulerTextExpr": "",
"SchedulerStartDateExpr": "",
"SchedulerEndDateExpr": "",
"Groups": [
{
"Caption": "",
@ -48,7 +61,7 @@
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"DbSourceType": 12,
"DbSourceType": 9,
"TurkishCaption": "Id",
"EnglishCaption": "Id",
"LookupDataSourceType": 1,
@ -64,7 +77,7 @@
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"DbSourceType": 12,
"DbSourceType": 9,
"TurkishCaption": "Branch Id",
"EnglishCaption": "Branch Id",
"LookupDataSourceType": 2,
@ -80,7 +93,7 @@
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"DbSourceType": 12,
"DbSourceType": 16,
"TurkishCaption": "Name",
"EnglishCaption": "Name",
"LookupDataSourceType": 1,
@ -96,7 +109,7 @@
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"DbSourceType": 12,
"DbSourceType": 9,
"TurkishCaption": "Parent Id",
"EnglishCaption": "Parent Id",
"LookupDataSourceType": 2,

View file

@ -55,6 +55,26 @@ export interface ListFormWizardDto {
selectCommand: string
keyFieldName: string
keyFieldDbSourceType: number
// Tree options (required when defaultLayout = 'tree')
treeKeyExpr?: string
treeParentIdExpr?: string
treeAutoExpandAll?: boolean
// Gantt options (required when defaultLayout = 'gantt')
ganttKeyExpr?: string
ganttParentIdExpr?: string
ganttAutoExpandAll?: boolean
ganttTitleExpr?: string
ganttStartExpr?: string
ganttEndExpr?: string
ganttProgressExpr?: string
// Scheduler options (required when defaultLayout = 'scheduler')
schedulerTextExpr?: string
schedulerStartDateExpr?: string
schedulerEndDateExpr?: string
groups?: ListFormWizardColumnGroupDto[]
}

View file

@ -68,6 +68,19 @@ const initialValues: ListFormWizardDto = {
selectCommand: '',
keyFieldName: '',
keyFieldDbSourceType: DbTypeEnum.Int32,
treeKeyExpr: '',
treeParentIdExpr: '',
treeAutoExpandAll: false,
ganttKeyExpr: '',
ganttParentIdExpr: '',
ganttAutoExpandAll: false,
ganttTitleExpr: '',
ganttStartExpr: '',
ganttEndExpr: '',
ganttProgressExpr: '',
schedulerTextExpr: '',
schedulerStartDateExpr: '',
schedulerEndDateExpr: '',
}
const step1ValidationSchema = Yup.object().shape({
@ -123,6 +136,8 @@ const Wizard = () => {
const [dbObjects, setDbObjects] = useState<SqlObjectExplorerDto | null>(null)
const [isLoadingDbObjects, setIsLoadingDbObjects] = useState(false)
const [currentDataSource, setCurrentDataSource] = useState('')
// In edit mode, stores the selectCommand to load columns for once dbObjects is ready
const pendingEditColumnsRef = useRef<{ dsCode: string; selectCommand: string; selectCommandType: SelectCommandTypeEnum } | null>(null)
const loadDbObjects = async (dsCode: string) => {
if (!dsCode) {
@ -144,6 +159,35 @@ const Wizard = () => {
loadDbObjects(currentDataSource)
}, [currentDataSource])
// When dbObjects become available in edit mode, load columns for the saved selectCommand
useEffect(() => {
if (!dbObjects || !pendingEditColumnsRef.current) return
const { dsCode, selectCommand, selectCommandType } = pendingEditColumnsRef.current
pendingEditColumnsRef.current = null
// Find schema from dbObjects
const allObjects = [
...dbObjects.tables.map((t) => ({ name: t.tableName, schema: t.schemaName })),
...dbObjects.storedProcedures.map((p) => ({ name: p.objectName, schema: p.schemaName })),
...dbObjects.views.map((v) => ({ name: v.objectName, schema: v.schemaName })),
...dbObjects.functions.map((f) => ({ name: f.objectName, schema: f.schemaName })),
]
const match = allObjects.find((o) => o.name === selectCommand)
if (!match && selectCommandType !== SelectCommandTypeEnum.Query) return
const schema = match?.schema ?? 'dbo'
// Load columns without wiping groups/selectedColumns
;(async () => {
setIsLoadingColumns(true)
try {
const res = await sqlObjectManagerService.getTableColumns(dsCode, schema, selectCommand)
setSelectCommandColumns(res.data ?? [])
} catch {
setSelectCommandColumns([])
} finally {
setIsLoadingColumns(false)
}
})()
}, [dbObjects])
// ── Column List for KeyFieldName & Column Selector ──
const [selectCommandColumns, setSelectCommandColumns] = useState<DatabaseColumnDto[]>([])
const [isLoadingColumns, setIsLoadingColumns] = useState(false)
@ -322,8 +366,30 @@ const Wizard = () => {
selectCommand: w.selectCommand ?? '',
keyFieldName: w.keyFieldName ?? '',
keyFieldDbSourceType: w.keyFieldDbSourceType ?? DbTypeEnum.Int32,
treeKeyExpr: w.treeKeyExpr ?? '',
treeParentIdExpr: w.treeParentIdExpr ?? '',
treeAutoExpandAll: w.treeAutoExpandAll ?? false,
ganttKeyExpr: w.ganttKeyExpr ?? '',
ganttParentIdExpr: w.ganttParentIdExpr ?? '',
ganttAutoExpandAll: w.ganttAutoExpandAll ?? false,
ganttTitleExpr: w.ganttTitleExpr ?? '',
ganttStartExpr: w.ganttStartExpr ?? '',
ganttEndExpr: w.ganttEndExpr ?? '',
ganttProgressExpr: w.ganttProgressExpr ?? '',
schedulerTextExpr: w.schedulerTextExpr ?? '',
schedulerStartDateExpr: w.schedulerStartDateExpr ?? '',
schedulerEndDateExpr: w.schedulerEndDateExpr ?? '',
})
// Queue column load to run once dbObjects is available
if (w.dataSourceCode && w.selectCommand) {
pendingEditColumnsRef.current = {
dsCode: w.dataSourceCode,
selectCommand: w.selectCommand,
selectCommandType: w.selectCommandType ?? SelectCommandTypeEnum.Table,
}
}
// Restore datasource to trigger db objects load
if (w.dataSourceCode) {
setCurrentDataSource(w.dataSourceCode)

View file

@ -510,6 +510,116 @@ const WizardStep2 = ({
</div>
</div>
{/* ── Tree options (full-width, shown when defaultLayout = 'tree') ─── */}
{values.defaultLayout === 'tree' && (
<div className="flex flex-wrap gap-6 mt-2 p-4 rounded-lg border border-indigo-200 dark:border-indigo-800 bg-indigo-50 dark:bg-indigo-900/20">
<h6 className="w-full text-sm font-semibold text-indigo-700 dark:text-indigo-300 mb-1">
{translate('::ListForms.ListFormEdit.DetailsLayoutDto.TreeLayout') || 'Tree Ayarları'}
</h6>
<FormItem
label={translate('::App.Listform.ListformField.KeyFieldName')}
className="min-w-[180px]"
>
<Field name="treeKeyExpr">
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
isClearable
options={selectCommandColumns.map((c) => ({ label: c.columnName, value: c.columnName }))}
value={values.treeKeyExpr ? { label: values.treeKeyExpr, value: values.treeKeyExpr } : null}
onChange={(opt: any) => form.setFieldValue(field.name, opt?.value ?? '')}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::ListForms.ListFormEdit.ParentIdExpr')}
className="min-w-[180px]"
>
<Field name="treeParentIdExpr">
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
isClearable
options={selectCommandColumns.map((c) => ({ label: c.columnName, value: c.columnName }))}
value={values.treeParentIdExpr ? { label: values.treeParentIdExpr, value: values.treeParentIdExpr } : null}
onChange={(opt: any) => form.setFieldValue(field.name, opt?.value ?? '')}
/>
)}
</Field>
</FormItem>
<FormItem label={translate('::ListForms.ListFormEdit.AutoExpandAll')}>
<Field name="treeAutoExpandAll" component={Checkbox} />
</FormItem>
</div>
)}
{/* ── Gantt options (full-width, shown when defaultLayout = 'gantt') ─── */}
{values.defaultLayout === 'gantt' && (
<div className="flex flex-wrap gap-6 mt-2 p-4 rounded-lg border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900/20">
<h6 className="w-full text-sm font-semibold text-amber-700 dark:text-amber-300 mb-1">
{translate('::ListForms.ListFormEdit.DetailsLayoutDto.GanttLayout') || 'Gantt Ayarları'}
</h6>
{[
{ name: 'ganttKeyExpr', label: translate('::App.Listform.ListformField.KeyFieldName') },
{ name: 'ganttParentIdExpr', label: translate('::ListForms.ListFormEdit.ParentIdExpr') },
{ name: 'ganttTitleExpr', label: translate('::ListForms.ListFormEdit.TitleExpr') },
{ name: 'ganttStartExpr', label: translate('::ListForms.ListFormEdit.StartExpr') },
{ name: 'ganttEndExpr', label: translate('::ListForms.ListFormEdit.EndExpr') },
{ name: 'ganttProgressExpr', label: translate('::ListForms.ListFormEdit.ProgressExpr') },
].map(({ name, label }) => (
<FormItem key={name} label={label} className="min-w-[180px]">
<Field name={name}>
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
isClearable
options={selectCommandColumns.map((c) => ({ label: c.columnName, value: c.columnName }))}
value={(values as any)[name] ? { label: (values as any)[name], value: (values as any)[name] } : null}
onChange={(opt: any) => form.setFieldValue(field.name, opt?.value ?? '')}
/>
)}
</Field>
</FormItem>
))}
<FormItem label={translate('::ListForms.ListFormEdit.AutoExpandAll')}>
<Field name="ganttAutoExpandAll" component={Checkbox} />
</FormItem>
</div>
)}
{/* ── Scheduler options (full-width, shown when defaultLayout = 'scheduler') ─── */}
{values.defaultLayout === 'scheduler' && (
<div className="flex flex-wrap gap-6 mt-2 p-4 rounded-lg border border-teal-200 dark:border-teal-800 bg-teal-50 dark:bg-teal-900/20">
<h6 className="w-full text-sm font-semibold text-teal-700 dark:text-teal-300 mb-1">
{translate('::ListForms.ListFormEdit.DetailsLayoutDto.SchedulerLayout') || 'Scheduler Ayarları'}
</h6>
{[
{ name: 'schedulerTextExpr', label: translate('::ListForms.SchedulerOptions.TextExpr') },
{ name: 'schedulerStartDateExpr', label: translate('::ListForms.SchedulerOptions.StartDateExpr') },
{ name: 'schedulerEndDateExpr', label: translate('::ListForms.SchedulerOptions.EndDateExpr') },
].map(({ name, label }) => (
<FormItem key={name} label={label} className="min-w-[180px]">
<Field name={name}>
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
isClearable
options={selectCommandColumns.map((c) => ({ label: c.columnName, value: c.columnName }))}
value={(values as any)[name] ? { label: (values as any)[name], value: (values as any)[name] } : null}
onChange={(opt: any) => form.setFieldValue(field.name, opt?.value ?? '')}
/>
)}
</Field>
</FormItem>
))}
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
<FormItem
label={translate('::ListForms.Wizard.Step2.TitleTextEnglish')}

View file

@ -36,18 +36,27 @@ function isLayoutValid(dto: GridDto, layout: ListViewLayoutType | undefined): bo
if (layout === 'pivot') return !!dto.gridOptions?.layoutDto?.pivot
if (layout === 'chart') return !!dto.gridOptions?.layoutDto?.chart
if (layout === 'tree')
return !!(dto.gridOptions?.layoutDto?.tree && dto.gridOptions?.treeOptionDto?.parentIdExpr)
return !!(
dto.gridOptions?.layoutDto?.tree &&
dto.gridOptions?.treeOptionDto?.keyExpr &&
dto.gridOptions?.treeOptionDto?.parentIdExpr
)
if (layout === 'gantt')
return !!(
dto.gridOptions?.layoutDto?.gantt &&
dto.gridOptions?.ganttOptionDto?.keyExpr &&
dto.gridOptions?.ganttOptionDto?.parentIdExpr &&
dto.gridOptions?.ganttOptionDto?.titleExpr
dto.gridOptions?.ganttOptionDto?.titleExpr &&
dto.gridOptions?.ganttOptionDto?.startExpr &&
dto.gridOptions?.ganttOptionDto?.endExpr &&
dto.gridOptions?.ganttOptionDto?.progressExpr
)
if (layout === 'scheduler')
return !!(
dto.gridOptions?.layoutDto?.scheduler &&
dto.gridOptions?.schedulerOptionDto?.textExpr &&
dto.gridOptions?.schedulerOptionDto?.startDateExpr
dto.gridOptions?.schedulerOptionDto?.startDateExpr &&
dto.gridOptions?.schedulerOptionDto?.endDateExpr
)
return false
}
@ -92,7 +101,7 @@ const List: React.FC = () => {
const savedLayout = states.find((s) => s.listFormCode === listFormCode)?.layout
const defaultLayout = gridDto.gridOptions?.layoutDto?.defaultLayout
const candidate = savedLayout ?? defaultLayout
setViewMode(isLayoutValid(gridDto, candidate) ? candidate : 'grid')
}, [gridDto, states, listFormCode])