Wizard kısmında sütunların Popupformda görünüp görünmeyeceğini belirlenmesi

This commit is contained in:
Sedat Öztürk 2026-06-06 22:52:56 +03:00
parent 64084679e8
commit 27e65f05f0
11 changed files with 619 additions and 74 deletions

View file

@ -12,6 +12,7 @@ public class WizardColumnItemInputDto
public string EditorScript { get; set; }
public int ColSpan { get; set; } = 1;
public bool IsRequired { get; set; }
public bool IncludeInEditingForm { get; set; } = true;
public DbType DbSourceType { get; set; } = DbType.String;
public string TurkishCaption { get; set; }
public string EnglishCaption { get; set; }

View file

@ -238,7 +238,7 @@ public class ListFormWizardAppService(
ColSpan = g.ColCount,
ItemType = "group",
Items = g.Items
.Where(i => i.DataField != input.KeyFieldName)
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
.Select((it, ii) => new EditingFormItemDto
{
Order = ii + 1,
@ -251,6 +251,7 @@ public class ListFormWizardAppService(
})
.ToArray()
})
.Where(g => g.Items.Length > 0)
.ToList();
//ListForm - varsa sil, yeniden ekle
@ -305,7 +306,7 @@ public class ListFormWizardAppService(
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.Widgets.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
PermissionJson = WizardConsts.DefaultPermissionJson(code),
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,

View file

@ -3735,8 +3735,8 @@
{
"resourceName": "Platform",
"key": "ListForms.ListForm.AddNewRecord",
"en": "Add New Record",
"tr": "Yeni Kayıt Ekle"
"en": "Add",
"tr": "Ekle"
},
{
"resourceName": "Platform",
@ -17474,6 +17474,12 @@
"en": "Columns load after selecting a Select Command",
"tr": "Select Command seçince sütunlar yüklenir"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step3.IncludeInEditingForm",
"en": "Include in Editing Form",
"tr": "Düzenleme Formunda Dahil Et"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step3.GenerateFromTable",
@ -17714,6 +17720,12 @@
"en": "Key Field Type",
"tr": "Anahtar Alan Tipi"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.ColumnsAndFormLayout",
"en": "Columns & Form Layout",
"tr": "Sütunlar ve Form Yerleşimi"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.SelectedColumns",
@ -17738,6 +17750,24 @@
"en": "Field",
"tr": "Alan"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.EditingForm",
"en": "Popup Form",
"tr": "Popup Form"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.EditingFormColumns",
"en": "Popup Form Columns",
"tr": "Popup Form Sütunları"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.UngroupedColumns",
"en": "Ungrouped Columns",
"tr": "Gruplanmamış Sütunlar"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step4.DeployAndSave",

View file

@ -4025,7 +4025,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
HeaderFilterJson = DefaultHeaderFilterJson,
SearchPanelJson = DefaultSearchPanelJson,
GroupPanelJson = DefaultGroupPanelJson,
SelectionJson = DefaultSelectionMultipleJson,
SelectionJson = DefaultSelectionSingleJson,
ColumnOptionJson = DefaultColumnOptionJson(false),
PermissionJson = DefaultPermissionJson(listFormName),
PagerOptionJson = DefaultPagerOptionJson,

View file

@ -0,0 +1,25 @@
IF OBJECT_ID(N'[dbo].[Sal_T_Approval]', 'U') IS NULL
BEGIN
CREATE TABLE [dbo].[Sal_T_Approval]
(
[Id] uniqueidentifier NOT NULL DEFAULT NEWID(),
[CreationTime] datetime2 NOT NULL DEFAULT GETUTCDATE(),
[CreatorId] uniqueidentifier NULL,
[LastModificationTime] datetime2 NULL,
[LastModifierId] uniqueidentifier NULL,
[IsDeleted] bit NOT NULL DEFAULT 0,
[DeletionTime] datetime2 NULL,
[DeleterId] uniqueidentifier NULL,
[TenantId] uniqueidentifier NULL,
[ApprovalUserName] nvarchar(256) NULL,
[ApprovalStatus] nvarchar(50) NULL,
[ApprovalDate] datetime NULL,
[ApprovalDescription] nvarchar(200) NULL,
[Name] nvarchar(100) NULL,
CONSTRAINT [PK_Sal_T_Approval] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO

View file

@ -0,0 +1,384 @@
{
"Wizard": {
"WizardName": "Approval",
"ListFormCode": "App.Wizard.Approval",
"MenuCode": "App.Wizard.Approval",
"IsTenant": true,
"IsBranch": false,
"IsOrganizationUnit": false,
"AllowAdding": true,
"AllowUpdating": true,
"AllowDeleting": true,
"AllowDetail": false,
"ConfirmDelete": true,
"DefaultLayout": "grid",
"Grid": true,
"Pivot": true,
"Tree": true,
"Chart": true,
"Gantt": true,
"Scheduler": true,
"LanguageTextMenuEn": "Approval",
"LanguageTextMenuTr": "Approval",
"LanguageTextTitleEn": "Approval",
"LanguageTextTitleTr": "Approval",
"LanguageTextDescEn": "Approval",
"LanguageTextDescTr": "Approval",
"LanguageTextMenuParentEn": "",
"LanguageTextMenuParentTr": "",
"PermissionGroupName": "App.Wizard.Sales",
"MenuParentCode": "App.Wizard.Sales",
"MenuParentIcon": "FcAssistant",
"MenuIcon": "FcBrokenLink",
"DataSourceCode": "Default",
"DataSourceConnectionString": "",
"SelectCommandType": 1,
"SelectCommand": "Sal_T_Approval",
"KeyFieldName": "Id",
"KeyFieldDbSourceType": 9,
"TreeKeyExpr": "",
"TreeParentIdExpr": "",
"TreeAutoExpandAll": false,
"GanttKeyExpr": "",
"GanttParentIdExpr": "",
"GanttAutoExpandAll": false,
"GanttTitleExpr": "",
"GanttStartExpr": "",
"GanttEndExpr": "",
"GanttProgressExpr": "",
"SchedulerTextExpr": "",
"SchedulerStartDateExpr": "",
"SchedulerEndDateExpr": "",
"Groups": [
{
"Caption": "",
"ColCount": 1,
"Items": [
{
"DataField": "Id",
"CaptionName": "App.Listform.ListformField.Id",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": true,
"IncludeInEditingForm": true,
"DbSourceType": 9,
"TurkishCaption": "Id",
"EnglishCaption": "Id",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"DataField": "Name",
"CaptionName": "App.Listform.ListformField.Name",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": true,
"DbSourceType": 16,
"TurkishCaption": "Name",
"EnglishCaption": "Name",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"DataField": "ApprovalUserName",
"CaptionName": "App.Listform.ListformField.ApprovalUserName",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": false,
"DbSourceType": 16,
"TurkishCaption": "Approval User Name",
"EnglishCaption": "Approval User Name",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"DataField": "ApprovalStatus",
"CaptionName": "App.Listform.ListformField.ApprovalStatus",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": false,
"DbSourceType": 16,
"TurkishCaption": "Approval Status",
"EnglishCaption": "Approval Status",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"DataField": "ApprovalDate",
"CaptionName": "App.Listform.ListformField.ApprovalDate",
"EditorType": "dxDateBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": false,
"DbSourceType": 6,
"TurkishCaption": "Approval Date",
"EnglishCaption": "Approval Date",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
},
{
"DataField": "ApprovalDescription",
"CaptionName": "App.Listform.ListformField.ApprovalDescription",
"EditorType": "dxTextBox",
"EditorOptions": "",
"EditorScript": "",
"ColSpan": 1,
"IsRequired": false,
"IncludeInEditingForm": false,
"DbSourceType": 16,
"TurkishCaption": "Approval Description",
"EnglishCaption": "Approval Description",
"LookupDataSourceType": 1,
"ValueExpr": "Key",
"DisplayExpr": "Name",
"LookupQuery": ""
}
]
}
],
"SubForms": [],
"Widgets": [],
"Workflow": {
"ApprovalUserFieldName": "ApprovalUserName",
"ApprovalDateFieldName": "ApprovalDate",
"ApprovalStatusFieldName": "ApprovalStatus",
"ApprovalDescriptionFieldName": "ApprovalDescription",
"Criteria": [
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Start",
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Ba\u015Flat",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "",
"NextOnStart": "WF1780768059929317",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 53,
"PositionY": 42,
"CompareOutcomes": [],
"Id": "WF1780768057089996"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Approval",
"Title": "Onay1",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "admin@sozsoft.com",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "WF1780768062793138",
"NextOnReject": "WF1780768065817524",
"PositionX": 356,
"PositionY": 41,
"CompareOutcomes": [],
"Id": "WF1780768059929317"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Approval",
"Title": "Onay2",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "demo@sozsoft.com",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "WF1780768065817524",
"NextOnReject": "WF1780768065817524",
"PositionX": 632,
"PositionY": 38,
"CompareOutcomes": [],
"Id": "WF1780768062793138"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Inform",
"Title": "Bilgilendirme",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "system@sozsoft.com",
"NextOnStart": "WF1780768071444394",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 472,
"PositionY": 417,
"CompareOutcomes": [],
"Id": "WF1780768065817524"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "End",
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Bitir",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 850,
"PositionY": 417,
"CompareOutcomes": [],
"Id": "WF1780768071444394"
}
]
},
"WorkflowCriteria": [
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Start",
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Ba\u015Flat",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "",
"NextOnStart": "WF1780768059929317",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 53,
"PositionY": 42,
"CompareOutcomes": [],
"Id": "WF1780768057089996"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Approval",
"Title": "Onay1",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "admin@sozsoft.com",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "WF1780768062793138",
"NextOnReject": "WF1780768065817524",
"PositionX": 356,
"PositionY": 41,
"CompareOutcomes": [],
"Id": "WF1780768059929317"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Approval",
"Title": "Onay2",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "demo@sozsoft.com",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "WF1780768065817524",
"NextOnReject": "WF1780768065817524",
"PositionX": 632,
"PositionY": 38,
"CompareOutcomes": [],
"Id": "WF1780768062793138"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "Inform",
"Title": "Bilgilendirme",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "system@sozsoft.com",
"NextOnStart": "WF1780768071444394",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 472,
"PositionY": 417,
"CompareOutcomes": [],
"Id": "WF1780768065817524"
},
{
"ListFormCode": "App.Wizard.Approval",
"Kind": "End",
"Title": "\u0130\u015F Ak\u0131\u015F\u0131 Bitir",
"CompareColumn": "Price",
"CompareOperator": "\u003E",
"CompareValue": 5000,
"Approver": "",
"NextOnStart": "",
"NextOnTrue": "",
"NextOnFalse": "",
"NextOnApprove": "",
"NextOnReject": "",
"PositionX": 850,
"PositionY": 417,
"CompareOutcomes": [],
"Id": "WF1780768071444394"
}
]
},
"IsDeletedField": true,
"IsCreatedField": true,
"InsertedRecords": {
"LanguageKeys": [
"App.Wizard.Approval",
"App.Wizard.Approval.Title",
"App.Wizard.Approval.Desc",
"App.Listform.ListformField.ApprovalUserName",
"App.Listform.ListformField.ApprovalStatus",
"App.Listform.ListformField.ApprovalDate",
"App.Listform.ListformField.ApprovalDescription"
],
"PermissionGroupNames": [
"App.Wizard.Sales"
],
"PermissionNames": [
"App.Wizard.Approval",
"App.Wizard.Approval.Create",
"App.Wizard.Approval.Update",
"App.Wizard.Approval.Delete",
"App.Wizard.Approval.Export",
"App.Wizard.Approval.Import",
"App.Wizard.Approval.Note"
],
"MenuCodes": [
"App.Wizard.Approval"
],
"DataSourceCodes": []
}
}

View file

@ -296,7 +296,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
ColSpan = g.ColCount,
ItemType = "group",
Items = g.Items
.Where(i => i.DataField != input.KeyFieldName)
.Where(i => i.IncludeInEditingForm && i.DataField != input.KeyFieldName)
.Select((it, ii) => new EditingFormItemDto
{
Order = ii + 1,
@ -309,6 +309,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
})
.ToArray()
})
.Where(g => g.Items.Length > 0)
.ToList();
// ListForm - varsa sil, yeniden ekle
@ -352,7 +353,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
HeaderFilterJson = WizardConsts.DefaultHeaderFilterJson,
SearchPanelJson = WizardConsts.DefaultSearchPanelJson,
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.Widgets.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
SelectionJson = WizardConsts.DefaultSelectionSingleJson(input.WorkflowCriteria.Count > 0 ? GridOptions.SelectionModeSingle : GridOptions.SelectionModeNone),
ColumnOptionJson = WizardConsts.DefaultColumnOptionJson(),
PermissionJson = WizardConsts.DefaultPermissionJson(code),
DeleteCommand = isDeleted ? WizardConsts.DefaultDeleteCommand(input.SelectCommand) : null,

View file

@ -14,6 +14,7 @@ export interface ListFormWizardColumnItemDto {
editorScript: string
colSpan: number
isRequired: boolean
includeInEditingForm: boolean
dbSourceType: number
}
@ -114,6 +115,7 @@ export interface WizardSeedFileItemDto {
editorScript: string
colSpan: number
isRequired: boolean
includeInEditingForm?: boolean
dbSourceType: number
turkishCaption?: string
englishCaption?: string

View file

@ -475,6 +475,7 @@ const Wizard = () => {
editorScript: it.editorScript ?? '',
colSpan: it.colSpan ?? 1,
isRequired: it.isRequired ?? false,
includeInEditingForm: it.includeInEditingForm ?? true,
turkishCaption: it.turkishCaption ?? it.dataField,
englishCaption: it.englishCaption ?? it.dataField,
captionName: it.captionName ?? `App.Listform.ListformField.${it.dataField}`,
@ -702,6 +703,7 @@ const Wizard = () => {
editorScript: item.editorScript ?? '',
colSpan: item.colSpan,
isRequired: item.isRequired,
includeInEditingForm: item.includeInEditingForm,
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12,
turkishCaption: item.turkishCaption,
englishCaption: item.englishCaption,

View file

@ -40,6 +40,7 @@ export interface WizardGroupItem {
editorScript: string
colSpan: number
isRequired: boolean
includeInEditingForm: boolean
turkishCaption?: string
englishCaption?: string
captionName?: string
@ -114,6 +115,7 @@ function newGroupItem(colName: string, meta?: DatabaseColumnDto): WizardGroupIte
editorScript: '',
colSpan: 1,
isRequired: meta?.isNullable === false,
includeInEditingForm: true,
turkishCaption: formatLabel(colName),
englishCaption: formatLabel(colName),
captionName: `App.Listform.ListformField.${colName}`,
@ -179,6 +181,7 @@ interface SortableItemProps {
onLookupQueryChange: (val: string) => void
onColSpanChange: (val: number) => void
onRequiredChange: (val: boolean) => void
onIncludeInEditingFormChange: (val: boolean) => void
onRemove: () => void
}
@ -194,6 +197,7 @@ function SortableItem({
onEditorScriptChange,
onColSpanChange,
onRequiredChange,
onIncludeInEditingFormChange,
onCaptionNameChange,
onLookupDataSourceTypeChange,
onDisplayExprChange,
@ -561,7 +565,7 @@ function SortableItem({
/>
</div>
{/* Bottom row: ColSpan + Required */}
{/* Bottom row: ColSpan + Editing Form + Required */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<span className="text-[10px] text-gray-400">
@ -579,6 +583,20 @@ function SortableItem({
))}
</select>
</div>
<label
className="flex items-center gap-1 cursor-pointer ml-auto"
title={translate('::ListForms.Wizard.Step3.IncludeInEditingForm') || 'Editing form'}
>
<input
type="checkbox"
checked={item.includeInEditingForm}
onChange={(e) => onIncludeInEditingFormChange(e.target.checked)}
className="w-3 h-3 accent-indigo-500"
/>
<span className="text-[10px] text-gray-400">
{translate('::ListForms.Wizard.Step3.IncludeInEditingForm') || 'Editing form'}
</span>
</label>
<label className="flex items-center gap-1 cursor-pointer ml-auto" title="Required">
<input
type="checkbox"
@ -723,6 +741,9 @@ function GroupCard({
onEditorScriptChange={(val) => onItemChange(item.id, { editorScript: val })}
onColSpanChange={(val) => onItemChange(item.id, { colSpan: val })}
onRequiredChange={(val) => onItemChange(item.id, { isRequired: val })}
onIncludeInEditingFormChange={(val) =>
onItemChange(item.id, { includeInEditingForm: val })
}
onLookupDataSourceTypeChange={(val: UiLookupDataSourceTypeEnum) =>
onItemChange(item.id, { lookupDataSourceType: val })
}

View file

@ -226,6 +226,13 @@ const WizardStep7 = ({
}
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
const editingFormFields = groups.flatMap((g) =>
g.items
.filter((item) => item.includeInEditingForm && item.dataField !== values.keyFieldName)
.map((item) => ({ ...item, groupCaption: g.caption })),
)
const groupedFieldNames = new Set(groups.flatMap((g) => g.items.map((item) => item.dataField)))
const ungroupedSelectedColumns = [...selectedColumns].filter((col) => !groupedFieldNames.has(col))
const hasWorkflowFields = Boolean(
workflow.approvalUserFieldName ||
workflow.approvalDateFieldName ||
@ -298,91 +305,157 @@ const WizardStep7 = ({
label={translate('::App.Listform.ListformField.ConnectionString')}
value={values.dataSourceConnectionString}
/>
<Row
label={translate('::ListForms.Wizard.Step4.CommandType')}
value={
selectCommandTypeOptions.find((o) => o.value === values.selectCommandType)?.label ||
values.selectCommandType
}
/>
<Row
label={translate('::ListForms.Wizard.Step4.SelectCommand')}
value={values.selectCommand}
value={`${values.selectCommand} (${
selectCommandTypeOptions.find((o: any) => o.value === values.selectCommandType)
?.label ?? String(values.selectCommandType)
})`}
/>
<Row
label={translate('::ListForms.Wizard.Step4.KeyField')}
value={values.keyFieldName}
/>
<Row
label={translate('::ListForms.Wizard.Step4.KeyFieldType')}
value={
value={`${values.keyFieldName} (${
dbSourceTypeOptions.find((o: any) => o.value === values.keyFieldDbSourceType)
?.label ?? String(values.keyFieldDbSourceType)
}
})`}
/>
</Section>
</div>
<Section
title={translate('::ListForms.Wizard.Step4.SelectedColumns')}
badge={selectedColumns.size}
title={
translate('::ListForms.Wizard.Step4.ColumnsAndFormLayout') || 'Columns & Form Layout'
}
badge={`${selectedColumns.size} ${translate('::App.Listform.ListformField.Column')} / ${editingFormFields.length} ${translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form'}`}
>
<div className="flex flex-wrap gap-1.5">
{[...selectedColumns].map((col) => {
const meta = selectCommandColumns.find((c) => c.columnName === col)
return (
<span
key={col}
className="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 border border-indigo-200 dark:border-indigo-700"
>
{col}
{meta?.dataType && (
<span className="text-[10px] text-indigo-400 opacity-70">{meta.dataType}</span>
)}
</span>
)
})}
</div>
</Section>
<Section title={translate('::ListForms.Wizard.Step4.FormGroups')} badge={groups.length}>
<div className="flex flex-col gap-3">
{groups.map((g) => (
<Section
key={g.id}
title={g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::App.Listform.ListformField.Column')}`}
defaultOpen={false}
<div className="mb-3 grid grid-cols-3 gap-2">
{[
{
label: translate('::ListForms.Wizard.Step4.SelectedColumns'),
value: selectedColumns.size,
className: 'text-indigo-600 dark:text-indigo-400',
},
{
label: translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form',
value: editingFormFields.length,
className: 'text-emerald-600 dark:text-emerald-400',
},
{
label: translate('::ListForms.Wizard.Step4.FormGroups'),
value: groups.length,
className: 'text-gray-700 dark:text-gray-200',
},
].map((item) => (
<div
key={item.label}
className="rounded-lg border border-gray-100 dark:border-gray-800 bg-gray-50 dark:bg-gray-800 px-3 py-2"
>
<div className="grid grid-cols-2 gap-2">
<div className={`text-lg font-semibold leading-none ${item.className}`}>
{item.value}
</div>
<div className="mt-1 text-[11px] text-gray-400 truncate">{item.label}</div>
</div>
))}
</div>
<div className="flex flex-col gap-2">
{groups.map((g) => (
<div
key={g.id}
className="overflow-hidden rounded-lg border border-gray-100 dark:border-gray-800"
>
<div className="flex items-center justify-between gap-3 bg-gray-50 dark:bg-gray-800 px-3 py-2">
<span className="text-xs font-semibold text-gray-700 dark:text-gray-200 truncate">
{g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
</span>
<div className="flex items-center gap-1.5 text-[10px] text-gray-400 shrink-0">
<span className="rounded bg-white dark:bg-gray-900 px-1.5 py-0.5">
{g.items.length} {translate('::ListForms.Wizard.Step4.StatField')}
</span>
<span className="rounded bg-white dark:bg-gray-900 px-1.5 py-0.5">
{g.colCount} {translate('::App.Listform.ListformField.Column')}
</span>
</div>
</div>
<div className="divide-y divide-gray-100 dark:divide-gray-800">
{g.items.length === 0 ? (
<span className="text-xs text-gray-300 italic">
<span className="block px-3 py-2 text-xs text-gray-300 italic">
{translate('::ListForms.Wizard.Step4.NoFields') || 'Alan yok'}
</span>
) : (
g.items.map((item) => (
<div
key={item.id}
className="flex items-center gap-2 py-1 border-b border-gray-100 dark:border-gray-800 last:border-0"
>
<span className="text-xs font-medium text-indigo-600 dark:text-indigo-400 w-36 shrink-0 truncate">
{item.dataField}
</span>
<span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
{item.editorType}
</span>
<span className="text-[10px] text-gray-400 mr-auto shrink-0 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
col-span-{item.colSpan}
{item.isRequired && (
<span className="ml-1 text-red-400 font-semibold">*</span>
)}
</span>
</div>
))
g.items.map((item) => {
const meta = selectCommandColumns.find((c) => c.columnName === item.dataField)
const isKeyField = item.dataField === values.keyFieldName
const isPopupField = item.includeInEditingForm && !isKeyField
return (
<div
key={item.id}
className="grid grid-cols-[minmax(150px,1fr)_auto] items-center gap-3 px-3 py-1.5"
>
<div className="flex min-w-0 items-center gap-2">
<span className="text-xs font-medium text-indigo-600 dark:text-indigo-400 truncate">
{item.dataField}
</span>
{meta?.dataType && (
<span className="text-[10px] text-gray-400 truncate">
{meta.dataType}
</span>
)}
</div>
<div className="flex flex-wrap justify-end gap-1">
<span className="rounded bg-indigo-50 dark:bg-indigo-900/30 px-1.5 py-0.5 text-[10px] text-indigo-600 dark:text-indigo-300">
{translate('::ListForms.Wizard.Step4.SelectedColumns') || 'List'}
</span>
{isPopupField && (
<span className="rounded bg-emerald-50 dark:bg-emerald-900/30 px-1.5 py-0.5 text-[10px] text-emerald-600 dark:text-emerald-300">
{translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form'}
</span>
)}
{isKeyField && (
<span className="rounded bg-amber-50 dark:bg-amber-900/30 px-1.5 py-0.5 text-[10px] text-amber-600 dark:text-amber-300">
{translate('::ListForms.Wizard.Step4.KeyField')}
</span>
)}
<span className="rounded bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 text-[10px] text-gray-400">
{item.editorType}
</span>
<span className="rounded bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 text-[10px] text-gray-400">
span-{item.colSpan}
{item.isRequired && <span className="ml-1 text-red-400">*</span>}
</span>
</div>
</div>
)
})
)}
</div>
</Section>
</div>
))}
{ungroupedSelectedColumns.length > 0 && (
<div className="rounded-lg border border-amber-100 dark:border-amber-900/40 bg-amber-50/50 dark:bg-amber-900/10 px-3 py-2">
<div className="mb-1.5 text-xs font-semibold text-amber-700 dark:text-amber-300">
{translate('::ListForms.Wizard.Step4.UngroupedColumns') || 'Ungrouped columns'}
</div>
<div className="flex flex-wrap gap-1.5">
{ungroupedSelectedColumns.map((col) => {
const meta = selectCommandColumns.find((c) => c.columnName === col)
return (
<span
key={col}
className="inline-flex items-center gap-1 rounded-full border border-amber-200 dark:border-amber-800 bg-white dark:bg-gray-900 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-300"
>
{col}
{meta?.dataType && (
<span className="text-[10px] text-amber-500 opacity-70">
{meta.dataType}
</span>
)}
</span>
)
})}
</div>
</div>
)}
</div>
</Section>
@ -473,7 +546,8 @@ const WizardStep7 = ({
</div>
{criteria.kind === 'Compare' && (
<div className="mt-1 text-[11px] text-gray-500 dark:text-gray-400">
{criteria.compareColumn} {criteria.compareOperator} {criteria.compareValue}
{criteria.compareColumn} {criteria.compareOperator}{' '}
{criteria.compareValue}
</div>
)}
{(criteria.kind === 'Approval' || criteria.kind === 'Inform') &&
@ -494,10 +568,14 @@ const WizardStep7 = ({
{/* ── Right: Deploy ──────────────────────────────────────────── */}
<div className="sticky top-4 flex flex-col gap-3 max-h-[calc(100vh-200px)]">
{/* Stats */}
<div className="grid grid-cols-3 gap-2">
<div className="grid grid-cols-7 gap-2">
{[
{ label: translate('::ListForms.Wizard.Step4.StatGroup'), value: groups.length },
{ label: translate('::ListForms.Wizard.Step4.StatField'), value: totalFields },
{
label: translate('::ListForms.Wizard.Step4.EditingForm') || 'Popup Form',
value: editingFormFields.length,
},
{
label: translate('::App.Listform.ListformField.Column'),
value: selectedColumns.size,