Wizard Güncellemeleri

This commit is contained in:
Sedat Öztürk 2026-03-18 22:47:39 +03:00
parent 63695be34e
commit e9f56e0e33
7 changed files with 143 additions and 8 deletions

View file

@ -11,4 +11,6 @@ public class WizardColumnItemInputDto
public int ColSpan { get; set; } = 1; public int ColSpan { get; set; } = 1;
public bool IsRequired { get; set; } public bool IsRequired { get; set; }
public DbType DbSourceType { get; set; } = DbType.String; public DbType DbSourceType { get; set; } = DbType.String;
public string TurkishCaption { get; set; }
public string EnglishCaption { get; set; }
} }

View file

@ -229,22 +229,31 @@ public class ListFormWizardAppService(
// ListFormField - each item in each group becomes a visible field record // ListFormField - each item in each group becomes a visible field record
var fieldOrder = 0; var fieldOrder = 0;
var captionName = string.Empty;
foreach (var group in input.Groups) foreach (var group in input.Groups)
{ {
foreach (var item in group.Items) foreach (var item in group.Items)
{ {
fieldOrder++; fieldOrder++;
captionName = $"App.Listform.ListformField.{item.DataField}";
await repoListFormField.InsertAsync(new ListFormField await repoListFormField.InsertAsync(new ListFormField
{ {
ListFormCode = input.ListFormCode, ListFormCode = input.ListFormCode,
FieldName = item.DataField, FieldName = item.DataField,
CaptionName = item.DataField, CaptionName = captionName,
Visible = item.DataField != input.KeyFieldName, Visible = item.DataField != input.KeyFieldName,
IsActive = true, IsActive = true,
AllowSearch = true,
ListOrderNo = fieldOrder, ListOrderNo = fieldOrder,
SourceDbType = item.DbSourceType, SourceDbType = item.DbSourceType,
CultureName = PlatformConsts.DefaultLanguage, CultureName = PlatformConsts.DefaultLanguage,
PermissionJson = WizardConsts.DefaultFieldPermissionJson(code),
ColumnCustomizationJson = WizardConsts.DefaultColumnCustomizationJson,
ColumnFilterJson = WizardConsts.DefaultColumnFilteringJson,
PivotSettingsJson = WizardConsts.DefaultPivotSettingsJson,
}, autoSave: false); }, autoSave: false);
await CreateLangKey(captionName, item.EnglishCaption, item.TurkishCaption);
} }
} }
} }

View file

@ -16304,6 +16304,18 @@
"en": "Columns load after selecting a Select Command", "en": "Columns load after selecting a Select Command",
"tr": "Select Command seçince sütunlar yüklenir" "tr": "Select Command seçince sütunlar yüklenir"
}, },
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step3.TurkishCaption",
"en": "Turkish Caption",
"tr": "Türkçe Başlık"
},
{
"resourceName": "Platform",
"key": "ListForms.Wizard.Step3.EnglishCaption",
"en": "English Caption",
"tr": "İngilizce Başlık"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "ListForms.Wizard.Step3.EditorOptions", "key": "ListForms.Wizard.Step3.EditorOptions",

View file

@ -2,6 +2,11 @@ using System.Data;
using System.Text.Json; using System.Text.Json;
using Sozsoft.Platform.Enums; using Sozsoft.Platform.Enums;
using static Sozsoft.Platform.PlatformConsts; using static Sozsoft.Platform.PlatformConsts;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using System.Globalization;
public static class WizardConsts public static class WizardConsts
{ {
@ -26,6 +31,44 @@ public static class WizardConsts
public static string MenuUrl(string code) => $"/admin/list/{code}"; public static string MenuUrl(string code) => $"/admin/list/{code}";
public static string MenuIcon => "FcList"; public static string MenuIcon => "FcList";
public static class LabelHelper
{
public static string GetCamelCaseLabel<T>(string propertyName)
{
var prop = typeof(T).GetProperty(propertyName);
if (prop == null)
return propertyName;
// 1. Display varsa direkt onu kullan
var displayAttr = prop.GetCustomAttribute<DisplayAttribute>();
if (displayAttr != null && !string.IsNullOrWhiteSpace(displayAttr.Name))
return displayAttr.Name;
// 2. CamelCase → kelimelere ayır
var words = Regex
.Split(propertyName, "(?=[A-Z])")
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToList();
// 3. Türkçe karakter dönüşümü + düzgün büyük harf
var culture = new CultureInfo("tr-TR");
words = words.Select(w =>
{
w = w.ToLower(culture);
// Türkçe karakter normalize (genel yaklaşım)
w = w
.Replace("i", "i") // bilinçli bırakıyoruz
.Replace("ı", "ı"); // zaten doğru
return culture.TextInfo.ToTitleCase(w);
}).ToList();
return string.Join(" ", words);
}
}
public static readonly string DefaultExportJson = JsonSerializer.Serialize(new public static readonly string DefaultExportJson = JsonSerializer.Serialize(new
{ {
Enabled = true, Enabled = true,
@ -83,6 +126,33 @@ public static class WizardConsts
}); });
} }
public static string DefaultFieldPermissionJson(string permissionName)
{
return JsonSerializer.Serialize(new
{
C = permissionName + ".Create",
R = permissionName,
U = permissionName + ".Update",
E = true,
I = false,
Deny = false
});
}
public static readonly string DefaultColumnCustomizationJson = JsonSerializer.Serialize(new
{
AllowReordering = true,
});
public static readonly string DefaultColumnFilteringJson = JsonSerializer.Serialize(new
{
AllowFiltering = true,
});
public static readonly string DefaultPivotSettingsJson = JsonSerializer.Serialize(new
{
IsPivot = true
});
public static string DefaultDeleteCommand(string tableName) public static string DefaultDeleteCommand(string tableName)
{ {
return $"UPDATE \"{tableName}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id"; return $"UPDATE \"{tableName}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";

View file

@ -324,13 +324,14 @@ const Wizard = () => {
const col = selectCommandColumns.find((c) => c.columnName === item.dataField) const col = selectCommandColumns.find((c) => c.columnName === item.dataField)
return { return {
dataField: item.dataField, dataField: item.dataField,
captionName: `App.Listform.ListformField.${item.dataField}`,
editorType: item.editorType, editorType: item.editorType,
editorOptions: item.editorOptions ?? '', editorOptions: item.editorOptions ?? '',
editorScript: item.editorScript ?? '', editorScript: item.editorScript ?? '',
colSpan: item.colSpan, colSpan: item.colSpan,
isRequired: item.isRequired, isRequired: item.isRequired,
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12, // 12 = DbType.String dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12, // 12 = DbType.String
turkishCaption: item.turkishCaption,
englishCaption: item.englishCaption,
} }
}), }),
})), })),

View file

@ -27,6 +27,8 @@ export interface WizardGroupItem {
editorScript: string editorScript: string
colSpan: number colSpan: number
isRequired: boolean isRequired: boolean
turkishCaption?: string
englishCaption?: string
} }
export interface WizardGroup { export interface WizardGroup {
@ -70,6 +72,18 @@ function inferEditorType(sqlType: string): string {
return 'dxTextBox' return 'dxTextBox'
} }
const formatLabel = (text: string) => {
return text
// CamelCase → kelimelere ayır
.split(/(?=[A-Z])/)
.filter(Boolean)
.map(word =>
word.charAt(0).toUpperCase() +
word.slice(1).toLowerCase()
)
.join(" ");
};
function newGroupItem(colName: string, sqlType = ''): WizardGroupItem { function newGroupItem(colName: string, sqlType = ''): WizardGroupItem {
return { return {
id: `${colName}_${Date.now()}`, id: `${colName}_${Date.now()}`,
@ -79,6 +93,8 @@ function newGroupItem(colName: string, sqlType = ''): WizardGroupItem {
editorScript: '', editorScript: '',
colSpan: 1, colSpan: 1,
isRequired: false, isRequired: false,
turkishCaption: formatLabel(colName),
englishCaption: formatLabel(colName),
} }
} }
@ -123,6 +139,8 @@ function AvailableColumnChip({ colName }: { colName: string }) {
interface SortableItemProps { interface SortableItemProps {
item: WizardGroupItem item: WizardGroupItem
groupColCount: number groupColCount: number
onTurkishCaptionChange: (val: string) => void
onEnglishCaptionChange: (val: string) => void
onEditorTypeChange: (val: string) => void onEditorTypeChange: (val: string) => void
onEditorOptionsChange: (val: string) => void onEditorOptionsChange: (val: string) => void
onEditorScriptChange: (val: string) => void onEditorScriptChange: (val: string) => void
@ -134,6 +152,8 @@ interface SortableItemProps {
function SortableItem({ function SortableItem({
item, item,
groupColCount, groupColCount,
onTurkishCaptionChange,
onEnglishCaptionChange,
onEditorTypeChange, onEditorTypeChange,
onEditorOptionsChange, onEditorOptionsChange,
onEditorScriptChange, onEditorScriptChange,
@ -198,14 +218,33 @@ function SortableItem({
))} ))}
</select> </select>
{/* Turkish Caption */}
<div className="flex flex-col gap-0.5">
<span className="text-[10px] text-gray-400 font-medium">{translate('::ListForms.Wizard.Step3.TurkishCaption')}</span>
<input
value={item.turkishCaption}
onChange={(e) => onTurkishCaptionChange(e.target.value)}
className="w-full text-xs px-1.5 py-1 rounded border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 focus:outline-none focus:border-indigo-400 resize-none font-mono"
/>
</div>
{/* English Caption */}
<div className="flex flex-col gap-0.5">
<span className="text-[10px] text-gray-400 font-medium">{translate('::ListForms.Wizard.Step3.EnglishCaption')}</span>
<input
value={item.englishCaption}
onChange={(e) => onEnglishCaptionChange(e.target.value)}
className="w-full text-xs px-1.5 py-1 rounded border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 focus:outline-none focus:border-indigo-400 resize-none font-mono"
/>
</div>
{/* Editor Options */} {/* Editor Options */}
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<span className="text-[10px] text-gray-400 font-medium">{translate('::ListForms.Wizard.Step3.EditorOptions')}</span> <span className="text-[10px] text-gray-400 font-medium">{translate('::ListForms.Wizard.Step3.EditorOptions')}</span>
<textarea <input
value={item.editorOptions} value={item.editorOptions}
onChange={(e) => onEditorOptionsChange(e.target.value)} onChange={(e) => onEditorOptionsChange(e.target.value)}
placeholder='{"readOnly": false}' placeholder='{"readOnly": false}'
rows={2}
className="w-full text-xs px-1.5 py-1 rounded border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 focus:outline-none focus:border-indigo-400 resize-none font-mono" className="w-full text-xs px-1.5 py-1 rounded border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 focus:outline-none focus:border-indigo-400 resize-none font-mono"
/> />
</div> </div>
@ -360,6 +399,8 @@ function GroupCard({
key={item.id} key={item.id}
item={item} item={item}
groupColCount={group.colCount} groupColCount={group.colCount}
onTurkishCaptionChange={(val) => onItemChange(item.id, { turkishCaption: val })}
onEnglishCaptionChange={(val) => onItemChange(item.id, { englishCaption: val })}
onEditorTypeChange={(val) => onItemChange(item.id, { editorType: val })} onEditorTypeChange={(val) => onItemChange(item.id, { editorType: val })}
onEditorOptionsChange={(val) => onItemChange(item.id, { editorOptions: val })} onEditorOptionsChange={(val) => onItemChange(item.id, { editorOptions: val })}
onEditorScriptChange={(val) => onItemChange(item.id, { editorScript: val })} onEditorScriptChange={(val) => onItemChange(item.id, { editorScript: val })}
@ -405,7 +446,7 @@ const WizardStep3 = ({
? [ ? [
{ {
id: `grp_default_${Date.now()}`, id: `grp_default_${Date.now()}`,
caption: 'Group 1', caption: '',
colCount: 2, colCount: 2,
items: [] as WizardGroupItem[], items: [] as WizardGroupItem[],
}, },

View file

@ -269,7 +269,7 @@ const WizardStep4 = ({
badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::ListForms.Wizard.Step4.StatColumn')}`} badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::ListForms.Wizard.Step4.StatColumn')}`}
defaultOpen={false} defaultOpen={false}
> >
<div className="flex flex-col gap-0.5"> <div className="grid grid-cols-2 gap-2">
{g.items.length === 0 ? ( {g.items.length === 0 ? (
<span className="text-xs text-gray-300 italic">{translate('::ListForms.Wizard.Step4.NoFields') || 'Alan yok'}</span> <span className="text-xs text-gray-300 italic">{translate('::ListForms.Wizard.Step4.NoFields') || 'Alan yok'}</span>
) : ( ) : (
@ -284,8 +284,8 @@ const WizardStep4 = ({
<span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded"> <span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
{item.editorType} {item.editorType}
</span> </span>
<span className="text-[10px] text-gray-400 ml-auto shrink-0"> <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">
span:{item.colSpan} col-span-{item.colSpan}
{item.isRequired && ( {item.isRequired && (
<span className="ml-1 text-red-400 font-semibold">*</span> <span className="ml-1 text-red-400 font-semibold">*</span>
)} )}