diff --git a/configs/seeds/host/custom/RoleComponent.json b/configs/seeds/host/custom/RoleComponent.json
index 921304c1..4a7af2b4 100644
--- a/configs/seeds/host/custom/RoleComponent.json
+++ b/configs/seeds/host/custom/RoleComponent.json
@@ -1,11 +1,11 @@
{
- "GeneratedAt": "2026-08-21T11:15:50.5342376Z",
+ "GeneratedAt": "2026-08-21T13:25:05.4840601Z",
"CustomComponents": [
{
"Name": "RoleComponent",
"RoutePath": "/admin/roles",
- "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22type%22%3A%22Form%22%2C%22kind%22%3A%22layout%22%2C%22props%22%3A%7B%22selectEndpoint%22%3A%22source_5w_5ibpv%22%2C%22insertEndpoint%22%3A%22source_h3_484qa%22%2C%22updateEndpoint%22%3A%22source_gb_d9cvs%22%2C%22deleteEndpoint%22%3A%22source_0t_avok9%22%2C%22keyFieldName%22%3A%22id%22%2C%22collectionPath%22%3A%22%22%2C%22keySource%22%3A%22query%22%2C%22keyParamName%22%3A%22id%22%2C%22previewKeyValue%22%3A%22%22%2C%22autoLoad%22%3Atrue%2C%22showToolbar%22%3Atrue%2C%22gap%22%3A16%2C%22className%22%3A%22%22%2C%22title%22%3A%22%3A%3AAbpIdentity.Roles%22%2C%22columnCount%22%3A2%2C%22selectPermission%22%3A%22%22%2C%22insertPermission%22%3A%22%22%7D%2C%22events%22%3A%7B%22onLoad%22%3A%22%22%2C%22onRecordChange%22%3A%22%22%2C%22onFieldChange%22%3A%22%22%2C%22onNewRecord%22%3A%22%22%2C%22onModeChange%22%3A%22%22%2C%22onBeforeSave%22%3A%22%22%2C%22onAfterSave%22%3A%22%22%2C%22onBeforeDelete%22%3A%22%22%2C%22onAfterDelete%22%3A%22%22%2C%22onError%22%3A%22%22%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_msyfpfia_ifhdq%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Name%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22Name%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input1%22%7D%2C%7B%22id%22%3A%22cmp_msyjl707_crcn7%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Normalized%20Name%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22NormalizedName%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input2%22%7D%2C%7B%22id%22%3A%22cmp_msyjdodn_u3o74%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Concurrent%20Stamp%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ConcurrencyStamp%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input3%22%7D%2C%7B%22id%22%3A%22cmp_msyje9ys_2c08a%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Default%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsDefault%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox1%22%7D%2C%7B%22id%22%3A%22cmp_msyjebu2_27tqi%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Static%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsStatic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox2%22%7D%2C%7B%22id%22%3A%22cmp_msyjmetn_gojol%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Public%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22false%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsPublic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox3%22%7D%2C%7B%22id%22%3A%22cmp_msyjo0g8_oathk%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Entity%20Version%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22number%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%223%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22EntityVersion%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input4%22%7D%2C%7B%22id%22%3A%22cmp_msyjplfq_py8lz%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Extra%20Properties%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%7B%7D%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ExtraProperties%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input5%22%7D%5D%2C%22ref%22%3A%22form1%22%7D%2C%7B%22id%22%3A%22cmp_mt023r5u_rb2jv%22%2C%22type%22%3A%22Grid%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22hoverable%22%3Atrue%2C%22overflow%22%3Atrue%2C%22borderlessRow%22%3Afalse%2C%22compact%22%3Afalse%2C%22cols%22%3A%223%22%2C%22rows%22%3A%222%22%2C%22gap%22%3A%224%22%2C%22colGap%22%3A%224%22%2C%22rowGap%22%3A%224%22%2C%22responsive%22%3Atrue%2C%22smCols%22%3A%221%22%2C%22mdCols%22%3A%222%22%2C%22lgCols%22%3A%223%22%2C%22xlCols%22%3A%224%22%2C%22autoFit%22%3Afalse%2C%22minColWidth%22%3A%22200px%22%2C%22height%22%3A%22auto%22%2C%22className%22%3A%22mt-4%22%2C%22items%22%3A%5B%7B%22id%22%3A%22grid-item-1%22%2C%22content%22%3A%22Grid%20Item%201%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-blue-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-2%22%2C%22content%22%3A%22Grid%20Item%202%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A2%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-green-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-3%22%2C%22content%22%3A%22Grid%20Item%203%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-yellow-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-4%22%2C%22content%22%3A%22Grid%20Item%204%22%2C%22colSpan%22%3A2%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-purple-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-5%22%2C%22content%22%3A%22Grid%20Item%205%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-red-100%20p-4%20border%20rounded%22%7D%5D%2C%22dataColumns%22%3A%5B%22UserId%22%5D%2C%22columnLookups%22%3A%7B%22UserId%22%3A%7B%22sourceId%22%3A%22source_gq_0q1dz%22%2C%22path%22%3A%22%22%2C%22valueField%22%3A%22Id%22%2C%22textField%22%3A%22UserName%22%7D%7D%2C%22columnCaptions%22%3A%7B%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%22items%22%3A%7B%22sourceId%22%3A%22source_tq_veb4m%22%2C%22path%22%3A%22%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22grid1%22%7D%2C%7B%22id%22%3A%22cmp_mt0288i3_ebpj5%22%2C%22type%22%3A%22ListView%22%2C%22kind%22%3A%22platform%22%2C%22props%22%3A%7B%22listFormCode%22%3A%22AbpIdentity.Roles%22%2C%22height%22%3A%22520px%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_y9_tr73w%22%2C%22field%22%3A%22Id%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.Id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%2C%22className%22%3A%22mt-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22listView1%22%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%7B%22id%22%3A%22source_5w_5ibpv%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_i7_msox4%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_0t_avok9%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Delete%22%2C%22method%22%3A%22DELETE%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_h3_484qa%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Create%22%2C%22method%22%3A%22POST%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gb_d9cvs%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Update%22%2C%22method%22%3A%22PUT%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_tq_veb4m%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%22%2C%22responsePath%22%3A%22%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_sw_86e0q%22%2C%22field%22%3A%22RoleId%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%7D%2C%7B%22id%22%3A%22source_d2_bri5s%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gq_0q1dz%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_u8_nu1lu%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%5D%2C%22permissionCode%22%3A%22App.Wizard.RoleList%22%7D__*/\nconst RoleComponent = () => {\n const designerHasPermission = (permission) => {\n const name = String(permission || \"\").trim()\n if (!name) return true\n return typeof checkPermission === \"function\" ? checkPermission(name) === true : true\n }\n\n const getByPath = (value, path) => {\n if (!path) return value\n const readPath = (target, targetPath) => targetPath.split('.').filter(Boolean).reduce((current, key) => current?.[key], target)\n const directValue = readPath(value, path)\n if (directValue !== undefined) return directValue\n if (path.startsWith('result.')) return readPath(value, path.slice('result.'.length))\n return value?.result === undefined ? undefined : readPath(value.result, path)\n }\n\n const resolveLookupText = (lookup, value) => {\n if (!lookup || value === null || value === undefined || value === \"\") return undefined\n const rows = getByPath(lookup.rows, lookup.path)\n if (!Array.isArray(rows)) return undefined\n const key = String(value)\n const row = rows.find((item) => item && typeof item === \"object\" && String(getByPath(item, lookup.value) ?? \"\") === key)\n if (!row) return undefined\n const text = getByPath(row, lookup.text)\n return text === null || text === undefined || text === \"\" ? undefined : text\n }\n\n const localeCulture = () => (typeof document !== \"undefined\" && document.documentElement.lang) || \"en\"\n const localeDateOptions = { year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }\n const localeTimeOptions = { year: \"numeric\", month: \"2-digit\", day: \"2-digit\", hour: \"2-digit\", minute: \"2-digit\" }\n const localeIsoPattern = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n const formatLocaleValue = (value) => {\n if (value === null || value === undefined) return \"\"\n const culture = localeCulture()\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) return String(value)\n return new Intl.NumberFormat(culture, { useGrouping: false, maximumFractionDigits: 20 }).format(value)\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? \"\" : new Intl.DateTimeFormat(culture, localeTimeOptions).format(value)\n }\n if (typeof value !== \"string\") return String(value)\n const parts = localeIsoPattern.exec(value.trim())\n if (!parts) return value\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return value\n if (parts[4] === undefined) {\n const day = new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]))\n return new Intl.DateTimeFormat(culture, localeDateOptions).format(day)\n }\n return new Intl.DateTimeFormat(culture, localeTimeOptions).format(parsed)\n }\n\n const readSqlField = (record, field) => {\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return undefined\n if (field in record) return record[field]\n const matched = Object.keys(record).find((key) => key.toLowerCase() === String(field).toLowerCase())\n return matched === undefined ? undefined : record[matched]\n }\n const toSqlRows = (value, collectionPath) => {\n const source = collectionPath ? getByPath(value, collectionPath) : value\n const rows = Array.isArray(source)\n ? source\n : source && typeof source === \"object\"\n ? (Array.isArray(source.items) ? source.items : [source])\n : []\n return rows.filter((item) => item && typeof item === \"object\" && !Array.isArray(item))\n }\n const setSqlField = (record, path, value) => {\n const keys = String(path || \"\").split(\".\").filter(Boolean)\n if (!keys.length) return record\n const next = { ...(record || {}) }\n let target = next\n for (let index = 0; index < keys.length - 1; index += 1) {\n const child = target[keys[index]]\n target[keys[index]] = child && typeof child === \"object\" && !Array.isArray(child) ? { ...child } : {}\n target = target[keys[index]]\n }\n target[keys[keys.length - 1]] = value\n return next\n }\n const bindSqlUrl = (url, record, keyField) => {\n let keyBound = false\n const boundUrl = String(url).replace(/\\{([^}]+)\\}|(?<=\\/):([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, colon) => {\n const name = (braced || colon || \"\").trim()\n if (!name) return match\n const value = readSqlField(record, name) ?? readSqlField(record, keyField)\n if (value === undefined || value === null || value === \"\") return match\n if (name.toLowerCase() === String(keyField).toLowerCase()) keyBound = true\n return encodeURIComponent(String(value))\n })\n return { url: boundUrl, keyBound }\n }\n const appendQueryParam = (url, name, value) =>\n url + (url.includes(\"?\") ? \"&\" : \"?\") + encodeURIComponent(name) + \"=\" + encodeURIComponent(value)\n const hasSqlUrlParams = (url) => /\\{[^}]+\\}|(?<=\\/):[A-Za-z_][A-Za-z0-9_]*/.test(String(url))\n const readUrlKey = (source, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (source === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n return segments.length ? decodeURIComponent(segments[segments.length - 1]) : \"\"\n }\n const filterSqlRowsByKey = (rows, keyField, keyValue) => {\n if (!keyValue) return rows\n const matched = rows.filter((row) => String(readSqlField(row, keyField) ?? \"\") === String(keyValue))\n // No match means the endpoint already filtered server side, or the key column\n // is named differently — keeping the rows beats showing an empty form.\n return matched.length ? matched : rows\n }\n // Update payload: only the columns the user actually edited, plus the key.\n const toSqlChanges = (original, current, keyField) => {\n const changes = {}\n const isSame = (left, right) => JSON.stringify(left ?? null) === JSON.stringify(right ?? null)\n Object.keys(current || {}).forEach((column) => {\n if (!isSame(original ? original[column] : undefined, current[column])) changes[column] = current[column]\n })\n const keyColumn = Object.keys(current || {}).find((column) => column.toLowerCase() === String(keyField).toLowerCase())\n if (keyColumn !== undefined && changes[keyColumn] === undefined) changes[keyColumn] = current[keyColumn]\n return changes\n }\n const callSqlEndpoint = (target, record, keyField, payload) => {\n // The URL is always bound from the full record: a placeholder may reference a\n // column that the update payload does not carry.\n const bound = bindSqlUrl(target.url, record, keyField)\n const request = { url: bound.url, method: target.method }\n if (target.method === \"POST\" || target.method === \"PUT\") request.data = payload === undefined ? record : payload\n const keyValue = readSqlField(record, keyField)\n const hasKey = keyValue !== undefined && keyValue !== null && keyValue !== \"\"\n // The key still has to reach the endpoint when the URL carries no placeholder.\n if (!bound.keyBound && hasKey && target.method !== \"POST\") request.params = { [keyField]: keyValue }\n return apiService.fetchData(request)\n }\n // A new record starts at the top of the form: the first enabled editor takes the\n // cursor, so a Yeni click is immediately followed by typing.\n const focusFirstSqlField = (host) => {\n if (typeof window === \"undefined\") return\n window.requestAnimationFrame(() => {\n const field = host.current?.querySelector(\"input:not([type='hidden']):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])\")\n if (!field) return\n field.focus()\n if (typeof field.select === \"function\") field.select()\n })\n }\n // Defaults declared on the bound children; only a new record starts from them.\n const buildSqlDefaults = (fields) => fields.reduce((record, field) => setSqlField(record, field.path, resolveSqlDefault(field)), {})\n const resolveSqlDefault = (field) => {\n if (field.token !== \"today\" && field.token !== \"now\") return field.value\n const now = new Date()\n const pad = (value) => String(value).padStart(2, \"0\")\n const day = now.getFullYear() + \"-\" + pad(now.getMonth() + 1) + \"-\" + pad(now.getDate())\n if (field.token === \"today\" || field.dateOnly) return day\n return day + \"T\" + pad(now.getHours()) + \":\" + pad(now.getMinutes()) + \":\" + pad(now.getSeconds())\n }\n const toSqlErrorMessage = (error) => error?.response?.data?.error?.message || error?.response?.data?.message || error?.message || translate(\"::App.Platform.IslemBasarisiz\")\n\n // Value of a filter that reads the page URL: a query string parameter, or a\n // path segment — the one after a segment named like the parameter, otherwise\n // the last one, which is the /orders/{id} detail page shape.\n const readDesignerUrlValue = (kind, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (kind === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n if (!segments.length) return \"\"\n const index = name ? segments.findIndex((segment) => segment.toLowerCase() === String(name).toLowerCase()) : -1\n if (index >= 0 && index < segments.length - 1) return decodeURIComponent(segments[index + 1])\n return decodeURIComponent(segments[segments.length - 1])\n }\n // Filters are appended as query parameters: `Column` for an equality and\n // `Column.operator` for anything else, which is what the endpoint parses.\n // Returns \"\" when a required filter has no value yet — the caller must skip the\n // request instead of loading the unfiltered collection.\n const buildFilteredUrl = (url, filters) => {\n const parts = []\n for (const filter of filters || []) {\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return \"\"\n continue\n }\n parts.push(encodeURIComponent(filter.param) + \"=\" + encodeURIComponent(value))\n }\n if (!parts.length) return url\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + parts.join(\"&\")\n }\n\n const buildPlatformFilterParams = (filters) => {\n const expressions = []\n for (const filter of filters || []) {\n if (filter.kind === \"isnull\" || filter.kind === \"notnull\") {\n expressions.push([filter.field, filter.operator, null])\n continue\n }\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return null\n continue\n }\n if (filter.kind === \"in\") {\n const entries = value.split(\",\").map((entry) => entry.trim()).filter(Boolean).map((entry) => [filter.field, \"=\", entry])\n if (!entries.length) continue\n expressions.push(entries.reduce((result, entry) => (result ? [result, \"or\", entry] : entry), null))\n continue\n }\n expressions.push([filter.field, filter.operator, value])\n }\n const params = new URLSearchParams()\n const merged = expressions.reduce((result, expression) => (result ? [result, \"and\", expression] : expression), null)\n if (merged) params.set(\"filter\", JSON.stringify(merged))\n return params\n }\n\n const [data_source_5w_5ibpv, setData_source_5w_5ibpv] = React.useState(null)\n const [data_source_i7_msox4, setData_source_i7_msox4] = React.useState(null)\n const [data_source_0t_avok9] = React.useState(null)\n const [data_source_h3_484qa] = React.useState(null)\n const [data_source_gb_d9cvs] = React.useState(null)\n const [data_source_tq_veb4m, setData_source_tq_veb4m] = React.useState(null)\n const [data_source_d2_bri5s, setData_source_d2_bri5s] = React.useState(null)\n const [data_source_gq_0q1dz, setData_source_gq_0q1dz] = React.useState(null)\n const [data_source_u8_nu1lu, setData_source_u8_nu1lu] = React.useState(null)\n\n const sqlselect_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"GET\", responsePath: \"\" }\n const sqlinsert_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"POST\", responsePath: \"\" }\n const sqlupdate_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"PUT\", responsePath: \"\" }\n const sqldelete_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"DELETE\", responsePath: \"\" }\n const sqlCanSelect_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList\")\n const sqlCanInsert_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Create\")\n const sqlCanUpdate_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Update\")\n const sqlCanDelete_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Delete\")\n const sqlKey_cmp_msyfmegt_jkkn3 = \"id\"\n const sqlKeyParam_cmp_msyfmegt_jkkn3 = \"id\"\n const [data_cmp_msyfmegt_jkkn3, setData_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlOriginal_cmp_msyfmegt_jkkn3, setSqlOriginal_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlMode_cmp_msyfmegt_jkkn3, setSqlMode_cmp_msyfmegt_jkkn3] = React.useState(\"edit\")\n const [sqlIndex_cmp_msyfmegt_jkkn3, setSqlIndex_cmp_msyfmegt_jkkn3] = React.useState(0)\n const [sqlBusy_cmp_msyfmegt_jkkn3, setSqlBusy_cmp_msyfmegt_jkkn3] = React.useState(false)\n const [sqlError_cmp_msyfmegt_jkkn3, setSqlError_cmp_msyfmegt_jkkn3] = React.useState(\"\")\n // Container element, so New can hand the cursor to the first field of the form.\n const sqlHost_cmp_msyfmegt_jkkn3 = React.useRef(null)\n // The Select key can come from the page URL, which is how a detail page reads\n // /api/app/orders/{id} or /api/app/orders?id=… for a single record.\n const sqlUrlKey_cmp_msyfmegt_jkkn3 = readUrlKey(\"query\", sqlKeyParam_cmp_msyfmegt_jkkn3)\n const sqlSelectUrl_cmp_msyfmegt_jkkn3 = React.useMemo(() => {\n if (!sqlselect_cmp_msyfmegt_jkkn3) return \"\"\n const base = sqlUrlKey_cmp_msyfmegt_jkkn3\n ? (() => {\n const bound = bindSqlUrl(sqlselect_cmp_msyfmegt_jkkn3.url, { [sqlKeyParam_cmp_msyfmegt_jkkn3]: sqlUrlKey_cmp_msyfmegt_jkkn3 }, sqlKeyParam_cmp_msyfmegt_jkkn3)\n return bound.keyBound ? bound.url : appendQueryParam(bound.url, sqlKeyParam_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3)\n })()\n : sqlselect_cmp_msyfmegt_jkkn3.url\n return base\n }, [sqlUrlKey_cmp_msyfmegt_jkkn3])\n // A key in the page URL always narrows the result: a list endpoint ignores the\n // parameter server side, so the requested record is picked out here. Without a\n // key every row is kept and the navigation below takes over.\n const sqlRows_cmp_msyfmegt_jkkn3 = React.useMemo(\n () => filterSqlRowsByKey(toSqlRows(data_source_5w_5ibpv, \"\"), sqlKey_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3),\n [data_source_5w_5ibpv, sqlUrlKey_cmp_msyfmegt_jkkn3],\n )\n // A fresh result set resets the position; New mode is left untouched so an\n // unsaved draft is not overwritten by a re-render.\n React.useEffect(() => {\n setSqlIndex_cmp_msyfmegt_jkkn3((current) => (current < sqlRows_cmp_msyfmegt_jkkn3.length ? current : 0))\n }, [sqlRows_cmp_msyfmegt_jkkn3])\n React.useEffect(() => {\n const row = sqlRows_cmp_msyfmegt_jkkn3[sqlIndex_cmp_msyfmegt_jkkn3]\n if (!row) return\n setData_cmp_msyfmegt_jkkn3(row)\n setSqlOriginal_cmp_msyfmegt_jkkn3(row)\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n }, [sqlRows_cmp_msyfmegt_jkkn3, sqlIndex_cmp_msyfmegt_jkkn3])\n const sqlHasKey_cmp_msyfmegt_jkkn3 = (() => {\n const value = readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n return value !== undefined && value !== null && value !== \"\"\n })()\n const sqlSetField_cmp_msyfmegt_jkkn3 = React.useCallback((path, value) => setData_cmp_msyfmegt_jkkn3((current) => setSqlField(current, path, value)), [])\n const sqlReload_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n // Without the Read permission the container is not rendered at all; the\n // request is dropped here so it is not fired for a form nobody can see.\n if (!sqlCanSelect_cmp_msyfmegt_jkkn3) return\n if (!sqlSelectUrl_cmp_msyfmegt_jkkn3) return\n // Calling a URL that still holds a placeholder is a guaranteed 400, so the\n // missing key is reported in the component instead.\n if (hasSqlUrlParams(sqlSelectUrl_cmp_msyfmegt_jkkn3)) {\n throw new Error(translate(\"::App.Form.MissingKeyParam\", { 0: sqlKeyParam_cmp_msyfmegt_jkkn3 }))\n }\n const response = await apiService.fetchData({ url: sqlSelectUrl_cmp_msyfmegt_jkkn3, method: \"GET\" })\n setData_source_5w_5ibpv(getByPath(response.data, sqlselect_cmp_msyfmegt_jkkn3.responsePath))\n }, [sqlSelectUrl_cmp_msyfmegt_jkkn3])\n // Reload wrapped with the busy/error handling the toolbar and mount effect need,\n // so a failing Select is reported in the component instead of the console.\n const sqlRefresh_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }, [sqlReload_cmp_msyfmegt_jkkn3])\n const sqlNew_cmp_msyfmegt_jkkn3 = React.useCallback(() => {\n if (!sqlCanInsert_cmp_msyfmegt_jkkn3) return\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n // Defaults are resolved on every New, so an @today column carries the day the\n // record was opened rather than the day the component was generated.\n const sqlDraft_cmp_msyfmegt_jkkn3 = buildSqlDefaults([{\"path\":\"IsDefault\",\"value\":true},{\"path\":\"IsStatic\",\"value\":true},{\"path\":\"IsPublic\",\"value\":false},{\"path\":\"EntityVersion\",\"value\":3},{\"path\":\"ExtraProperties\",\"value\":\"{}\"}])\n setData_cmp_msyfmegt_jkkn3(sqlDraft_cmp_msyfmegt_jkkn3)\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n // After the re-render that paints the empty form, not before it.\n focusFirstSqlField(sqlHost_cmp_msyfmegt_jkkn3)\n }, [])\n const sqlPrev_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.max(0, current - 1)), [])\n const sqlNext_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.min(sqlRows_cmp_msyfmegt_jkkn3.length - 1, current + 1)), [sqlRows_cmp_msyfmegt_jkkn3])\n const sqlSave_cmp_msyfmegt_jkkn3 = async () => {\n const isNew = sqlMode_cmp_msyfmegt_jkkn3 === \"new\"\n const target = isNew ? sqlinsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3\n // The button is already hidden without the permission; a script calling save\n // through a ref must not be able to walk around it either.\n if (!(isNew ? sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlCanUpdate_cmp_msyfmegt_jkkn3)) return\n if (!target) {\n setSqlError_cmp_msyfmegt_jkkn3(isNew ? translate(\"::App.Form.InsertEndpointMissing\") : translate(\"::App.Form.UpdateEndpointMissing\"))\n return\n }\n // Update carries only the edited columns plus the key; Insert sends the record.\n const payload = isNew ? data_cmp_msyfmegt_jkkn3 : toSqlChanges(sqlOriginal_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n if (!isNew && Object.keys(payload).filter((column) => column.toLowerCase() !== String(sqlKey_cmp_msyfmegt_jkkn3).toLowerCase()).length === 0) {\n setSqlError_cmp_msyfmegt_jkkn3(translate(\"::App.Form.NoChanges\"))\n return\n }\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n const response = await callSqlEndpoint(target, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3, payload)\n // With a Select endpoint the reload is the source of truth and its effect\n // repopulates the form; without one, the response row is all there is.\n if (sqlselect_cmp_msyfmegt_jkkn3) {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } else {\n const saved = toSqlRows(response?.data, \"\")[0]\n if (saved) {\n setData_cmp_msyfmegt_jkkn3(saved)\n setSqlOriginal_cmp_msyfmegt_jkkn3(saved)\n }\n }\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n const sqlDelete_cmp_msyfmegt_jkkn3 = async () => {\n if (!sqlCanDelete_cmp_msyfmegt_jkkn3 || !sqldelete_cmp_msyfmegt_jkkn3 || !sqlHasKey_cmp_msyfmegt_jkkn3 || sqlMode_cmp_msyfmegt_jkkn3 === \"new\") return\n // Deleting cannot be undone from the form, so it always asks first.\n const confirmMessage = translate(\"::App.Form.DeleteConfirm\", { 0: sqlKey_cmp_msyfmegt_jkkn3 + \" = \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) })\n if (typeof window !== \"undefined\" && !window.confirm(confirmMessage)) return\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await callSqlEndpoint(sqldelete_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n setData_cmp_msyfmegt_jkkn3({})\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n React.useEffect(() => { void sqlRefresh_cmp_msyfmegt_jkkn3() }, [sqlRefresh_cmp_msyfmegt_jkkn3])\n\n const filters_source_tq_veb4m = [{ param: \"RoleId\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"id\"), required: true }]\n const filterKey_source_tq_veb4m = JSON.stringify(filters_source_tq_veb4m)\n React.useEffect(() => {\n const url = buildFilteredUrl(\"/api/app/crudendpoint/AbpUserRoles\", filters_source_tq_veb4m)\n // An unmet required filter leaves the component empty on purpose.\n if (!url) {\n setData_source_tq_veb4m(null)\n return\n }\n let active = true\n apiService.fetchData({ url, method: 'GET' }).then((response) => {\n if (active) setData_source_tq_veb4m(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUserRoles · GetList endpoint error:\", error))\n return () => { active = false }\n }, [filterKey_source_tq_veb4m])\n\n React.useEffect(() => {\n let active = true\n apiService.fetchData({ url: \"/api/app/crudendpoint/AbpUsers\", method: 'GET' }).then((response) => {\n if (active) setData_source_gq_0q1dz(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUsers · GetList endpoint error:\", error))\n return () => { active = false }\n }, [])\n\n const platformFilters_cmp_mt0288i3_ebpj5 = [{ field: \"Id\", operator: \"=\", kind: \"eq\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"Id\"), required: true }]\n const platformKey_cmp_mt0288i3_ebpj5 = JSON.stringify(platformFilters_cmp_mt0288i3_ebpj5)\n // The views keep the params object in their own state and write back to it, so\n // it must stay the same object until a filter value actually changes.\n const platformParams_cmp_mt0288i3_ebpj5 = React.useMemo(() => buildPlatformFilterParams(platformFilters_cmp_mt0288i3_ebpj5), [platformKey_cmp_mt0288i3_ebpj5])\n\n const handle_cmp_msyfpfia_ifhdq_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"Name\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjl707_crcn7_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"NormalizedName\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjdodn_u3o74_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ConcurrencyStamp\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyje9ys_2c08a_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsDefault\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjebu2_27tqi_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsStatic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjmetn_gojol_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsPublic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjo0g8_oathk_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"EntityVersion\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjplfq_py8lz_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ExtraProperties\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n return (\n <>\n {sqlCanSelect_cmp_msyfmegt_jkkn3 ? (\n {sqlHasKey_cmp_msyfmegt_jkkn3 ? sqlKey_cmp_msyfmegt_jkkn3 + \": \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) : sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : \"\"}}>\n \n
\n \n \n \n \n {\"Is Default\"}\n \n \n {\"Is Static\"}\n \n \n {\"Is Public\"}\n \n \n \n
\n
\n {sqlRows_cmp_msyfmegt_jkkn3.length > 1 ? (\n <>\n {translate(\"::App.Platform.Previous\")} \n {`${sqlIndex_cmp_msyfmegt_jkkn3 + 1} / ${sqlRows_cmp_msyfmegt_jkkn3.length}`} \n = sqlRows_cmp_msyfmegt_jkkn3.length - 1} onClick={sqlNext_cmp_msyfmegt_jkkn3}>{translate(\"::App.Platform.Next\")} \n \n >\n ) : null}\n {sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 ? {translate(\"::App.Platform.New\")} : null}\n {(sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3 && sqlCanUpdate_cmp_msyfmegt_jkkn3) ? { void sqlSave_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Save\")} : null}\n {sqldelete_cmp_msyfmegt_jkkn3 && sqlCanDelete_cmp_msyfmegt_jkkn3 ? { void sqlDelete_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Delete\")} : null}\n {sqlselect_cmp_msyfmegt_jkkn3 && sqlCanSelect_cmp_msyfmegt_jkkn3 ? { void sqlRefresh_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Refresh\")} : null}\n {sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : translate(\"::App.Platform.Editing\")} \n
\n {sqlError_cmp_msyfmegt_jkkn3 ?
{sqlError_cmp_msyfmegt_jkkn3}
: null}\n
\n \n ) : null}\n
\n {(() => {\n const gridItems_cmp_mt023r5u_rb2jv = (Array.isArray(getByPath(data_source_tq_veb4m, \"\")) ? getByPath(data_source_tq_veb4m, \"\") : Array.isArray(data_source_tq_veb4m) ? data_source_tq_veb4m : null)\n // Nothing loaded yet — an endpoint still in flight, or a required filter\n // holding the request back — is an empty grid, not a binding error.\n if (gridItems_cmp_mt023r5u_rb2jv === null || gridItems_cmp_mt023r5u_rb2jv === undefined) return null\n if (!Array.isArray(gridItems_cmp_mt023r5u_rb2jv)) return
Grid items bağlantısı bir koleksiyon döndürmelidir.
\n const gridColumns_cmp_mt023r5u_rb2jv = [\"UserId\"]\n const gridLookups_cmp_mt023r5u_rb2jv = { \"UserId\": { rows: data_source_gq_0q1dz, path: \"\", value: \"Id\", text: \"UserName\" } }\n if (!gridColumns_cmp_mt023r5u_rb2jv.length) return
Preview için en az bir sütun seçin.
\n return (\n
\n
\n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => {(column === \"value\" ? \"Value\" : column)} )}\n \n \n {gridItems_cmp_mt023r5u_rb2jv.map((gridItem_cmp_mt023r5u_rb2jv, rowIndex) => (\n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => {\n const cellValue_cmp_mt023r5u_rb2jv = column === \"value\" ? gridItem_cmp_mt023r5u_rb2jv : getByPath(gridItem_cmp_mt023r5u_rb2jv, column)\n const lookedUp = resolveLookupText(gridLookups_cmp_mt023r5u_rb2jv[column], cellValue_cmp_mt023r5u_rb2jv)\n const cellText = lookedUp === undefined ? cellValue_cmp_mt023r5u_rb2jv : lookedUp\n const text = typeof cellText === \"object\" && cellText !== null && !(cellText instanceof Date) ? JSON.stringify(cellText) : formatLocaleValue(cellText) || \"—\"\n return {text} \n })}\n \n ))}\n \n
\n
\n )\n })()}\n
\n {platformParams_cmp_mt0288i3_ebpj5 === null ? null : (\n \n )}\n >\n )\n}\n\nexport default RoleComponent",
- "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[{\"id\":\"cmp_msyfmegt_jkkn3\",\"type\":\"Form\",\"kind\":\"layout\",\"props\":{\"selectEndpoint\":\"source_5w_5ibpv\",\"insertEndpoint\":\"source_h3_484qa\",\"updateEndpoint\":\"source_gb_d9cvs\",\"deleteEndpoint\":\"source_0t_avok9\",\"keyFieldName\":\"id\",\"collectionPath\":\"\",\"keySource\":\"query\",\"keyParamName\":\"id\",\"previewKeyValue\":\"\",\"autoLoad\":true,\"showToolbar\":true,\"gap\":16,\"className\":\"\",\"title\":\"::AbpIdentity.Roles\",\"columnCount\":2,\"selectPermission\":\"\",\"insertPermission\":\"\"},\"events\":{\"onLoad\":\"\",\"onRecordChange\":\"\",\"onFieldChange\":\"\",\"onNewRecord\":\"\",\"onModeChange\":\"\",\"onBeforeSave\":\"\",\"onAfterSave\":\"\",\"onBeforeDelete\":\"\",\"onAfterDelete\":\"\",\"onError\":\"\"},\"bindings\":{},\"children\":[{\"id\":\"cmp_msyfpfia_ifhdq\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Name :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"Name\"}},\"children\":[],\"ref\":\"input1\"},{\"id\":\"cmp_msyjl707_crcn7\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Normalized Name :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"NormalizedName\"}},\"children\":[],\"ref\":\"input2\"},{\"id\":\"cmp_msyjdodn_u3o74\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Concurrent Stamp : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ConcurrencyStamp\"}},\"children\":[],\"ref\":\"input3\"},{\"id\":\"cmp_msyje9ys_2c08a\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Default\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsDefault\"}},\"children\":[],\"ref\":\"checkbox1\"},{\"id\":\"cmp_msyjebu2_27tqi\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Static\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsStatic\"}},\"children\":[],\"ref\":\"checkbox2\"},{\"id\":\"cmp_msyjmetn_gojol\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Public\",\"style\":{},\"sqlDefaultValue\":\"false\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsPublic\"}},\"children\":[],\"ref\":\"checkbox3\"},{\"id\":\"cmp_msyjo0g8_oathk\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Entity Version : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"number\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"3\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"EntityVersion\"}},\"children\":[],\"ref\":\"input4\"},{\"id\":\"cmp_msyjplfq_py8lz\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Extra Properties :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"{}\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ExtraProperties\"}},\"children\":[],\"ref\":\"input5\"}],\"ref\":\"form1\"},{\"id\":\"cmp_mt023r5u_rb2jv\",\"type\":\"Grid\",\"kind\":\"ui\",\"props\":{\"hoverable\":true,\"overflow\":true,\"borderlessRow\":false,\"compact\":false,\"cols\":\"3\",\"rows\":\"2\",\"gap\":\"4\",\"colGap\":\"4\",\"rowGap\":\"4\",\"responsive\":true,\"smCols\":\"1\",\"mdCols\":\"2\",\"lgCols\":\"3\",\"xlCols\":\"4\",\"autoFit\":false,\"minColWidth\":\"200px\",\"height\":\"auto\",\"className\":\"mt-4\",\"items\":[{\"id\":\"grid-item-1\",\"content\":\"Grid Item 1\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":1,\"rowStart\":1,\"className\":\"bg-blue-100 p-4 border rounded\"},{\"id\":\"grid-item-2\",\"content\":\"Grid Item 2\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":2,\"rowStart\":1,\"className\":\"bg-green-100 p-4 border rounded\"},{\"id\":\"grid-item-3\",\"content\":\"Grid Item 3\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":1,\"className\":\"bg-yellow-100 p-4 border rounded\"},{\"id\":\"grid-item-4\",\"content\":\"Grid Item 4\",\"colSpan\":2,\"rowSpan\":1,\"colStart\":1,\"rowStart\":2,\"className\":\"bg-purple-100 p-4 border rounded\"},{\"id\":\"grid-item-5\",\"content\":\"Grid Item 5\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":2,\"className\":\"bg-red-100 p-4 border rounded\"}],\"dataColumns\":[\"UserId\"],\"columnLookups\":{\"UserId\":{\"sourceId\":\"source_gq_0q1dz\",\"path\":\"\",\"valueField\":\"Id\",\"textField\":\"UserName\"}},\"columnCaptions\":{}},\"events\":{},\"bindings\":{\"items\":{\"sourceId\":\"source_tq_veb4m\",\"path\":\"\"}},\"children\":[],\"ref\":\"grid1\"},{\"id\":\"cmp_mt0288i3_ebpj5\",\"type\":\"ListView\",\"kind\":\"platform\",\"props\":{\"listFormCode\":\"AbpIdentity.Roles\",\"height\":\"520px\",\"filters\":[{\"id\":\"filter_y9_tr73w\",\"field\":\"Id\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.Id\",\"previewValue\":\"\",\"required\":true}],\"className\":\"mt-4\"},\"events\":{},\"bindings\":{},\"children\":[],\"ref\":\"listView1\"}],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[{\"id\":\"source_5w_5ibpv\",\"name\":\"AbpRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_i7_msox4\",\"name\":\"AbpRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_0t_avok9\",\"name\":\"AbpRoles · Delete\",\"method\":\"DELETE\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_h3_484qa\",\"name\":\"AbpRoles · Create\",\"method\":\"POST\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_gb_d9cvs\",\"name\":\"AbpRoles · Update\",\"method\":\"PUT\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_tq_veb4m\",\"name\":\"AbpUserRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles\",\"responsePath\":\"\",\"filters\":[{\"id\":\"filter_sw_86e0q\",\"field\":\"RoleId\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.id\",\"previewValue\":\"\",\"required\":true}]},{\"id\":\"source_d2_bri5s\",\"name\":\"AbpUserRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_gq_0q1dz\",\"name\":\"AbpUsers · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers\",\"responsePath\":\"\"},{\"id\":\"source_u8_nu1lu\",\"name\":\"AbpUsers · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers/{id}\",\"responsePath\":\"\"}],\"permissionCode\":\"App.Wizard.RoleList\"}}",
+ "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22type%22%3A%22Form%22%2C%22kind%22%3A%22layout%22%2C%22props%22%3A%7B%22selectEndpoint%22%3A%22source_5w_5ibpv%22%2C%22insertEndpoint%22%3A%22source_h3_484qa%22%2C%22updateEndpoint%22%3A%22source_gb_d9cvs%22%2C%22deleteEndpoint%22%3A%22source_0t_avok9%22%2C%22keyFieldName%22%3A%22id%22%2C%22collectionPath%22%3A%22%22%2C%22keySource%22%3A%22query%22%2C%22keyParamName%22%3A%22id%22%2C%22previewKeyValue%22%3A%22%22%2C%22autoLoad%22%3Atrue%2C%22showToolbar%22%3Atrue%2C%22gap%22%3A16%2C%22className%22%3A%22%22%2C%22title%22%3A%22%3A%3AAbpIdentity.Roles%22%2C%22columnCount%22%3A2%2C%22selectPermission%22%3A%22%22%2C%22insertPermission%22%3A%22%22%7D%2C%22events%22%3A%7B%22onLoad%22%3A%22%22%2C%22onRecordChange%22%3A%22%22%2C%22onFieldChange%22%3A%22%22%2C%22onNewRecord%22%3A%22%22%2C%22onModeChange%22%3A%22%22%2C%22onBeforeSave%22%3A%22%22%2C%22onAfterSave%22%3A%22%22%2C%22onBeforeDelete%22%3A%22%22%2C%22onAfterDelete%22%3A%22%22%2C%22onError%22%3A%22%22%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_msyfpfia_ifhdq%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22%3A%3AApp.Platform.Name%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22Name%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input1%22%7D%2C%7B%22id%22%3A%22cmp_msyjl707_crcn7%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Normalized%20Name%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22NormalizedName%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input2%22%7D%2C%7B%22id%22%3A%22cmp_msyjdodn_u3o74%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Concurrent%20Stamp%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ConcurrencyStamp%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input3%22%7D%2C%7B%22id%22%3A%22cmp_msyje9ys_2c08a%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Default%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsDefault%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox1%22%7D%2C%7B%22id%22%3A%22cmp_msyjebu2_27tqi%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Static%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsStatic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox2%22%7D%2C%7B%22id%22%3A%22cmp_msyjmetn_gojol%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Public%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22false%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsPublic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox3%22%7D%2C%7B%22id%22%3A%22cmp_msyjo0g8_oathk%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Entity%20Version%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22number%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%223%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22EntityVersion%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input4%22%7D%2C%7B%22id%22%3A%22cmp_msyjplfq_py8lz%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Extra%20Properties%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%7B%7D%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ExtraProperties%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input5%22%7D%5D%2C%22ref%22%3A%22form1%22%7D%2C%7B%22id%22%3A%22cmp_mt023r5u_rb2jv%22%2C%22type%22%3A%22Grid%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22hoverable%22%3Atrue%2C%22overflow%22%3Atrue%2C%22borderlessRow%22%3Afalse%2C%22compact%22%3Afalse%2C%22cols%22%3A%223%22%2C%22rows%22%3A%222%22%2C%22gap%22%3A%224%22%2C%22colGap%22%3A%224%22%2C%22rowGap%22%3A%224%22%2C%22responsive%22%3Atrue%2C%22smCols%22%3A%221%22%2C%22mdCols%22%3A%222%22%2C%22lgCols%22%3A%223%22%2C%22xlCols%22%3A%224%22%2C%22autoFit%22%3Afalse%2C%22minColWidth%22%3A%22200px%22%2C%22height%22%3A%22auto%22%2C%22className%22%3A%22mt-4%22%2C%22items%22%3A%5B%7B%22id%22%3A%22grid-item-1%22%2C%22content%22%3A%22Grid%20Item%201%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-blue-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-2%22%2C%22content%22%3A%22Grid%20Item%202%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A2%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-green-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-3%22%2C%22content%22%3A%22Grid%20Item%203%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-yellow-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-4%22%2C%22content%22%3A%22Grid%20Item%204%22%2C%22colSpan%22%3A2%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-purple-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-5%22%2C%22content%22%3A%22Grid%20Item%205%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-red-100%20p-4%20border%20rounded%22%7D%5D%2C%22dataColumns%22%3A%5B%22UserId%22%5D%2C%22columnLookups%22%3A%7B%22UserId%22%3A%7B%22sourceId%22%3A%22source_gq_0q1dz%22%2C%22path%22%3A%22%22%2C%22valueField%22%3A%22Id%22%2C%22textField%22%3A%22UserName%22%7D%7D%2C%22columnCaptions%22%3A%7B%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%22items%22%3A%7B%22sourceId%22%3A%22source_tq_veb4m%22%2C%22path%22%3A%22%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22grid1%22%7D%2C%7B%22id%22%3A%22cmp_mt0288i3_ebpj5%22%2C%22type%22%3A%22ListView%22%2C%22kind%22%3A%22platform%22%2C%22props%22%3A%7B%22listFormCode%22%3A%22AbpIdentity.Roles%22%2C%22height%22%3A%22520px%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_y9_tr73w%22%2C%22field%22%3A%22Id%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.Id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%2C%22className%22%3A%22mt-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22listView1%22%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%7B%22id%22%3A%22source_5w_5ibpv%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_i7_msox4%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_0t_avok9%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Delete%22%2C%22method%22%3A%22DELETE%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_h3_484qa%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Create%22%2C%22method%22%3A%22POST%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gb_d9cvs%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Update%22%2C%22method%22%3A%22PUT%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_tq_veb4m%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%22%2C%22responsePath%22%3A%22%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_sw_86e0q%22%2C%22field%22%3A%22RoleId%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%7D%2C%7B%22id%22%3A%22source_d2_bri5s%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gq_0q1dz%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_u8_nu1lu%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%5D%2C%22permissionCode%22%3A%22App.Wizard.RoleList%22%7D__*/\nconst RoleComponent = () => {\n const designerHasPermission = (permission) => {\n const name = String(permission || \"\").trim()\n if (!name) return true\n return typeof checkPermission === \"function\" ? checkPermission(name) === true : true\n }\n\n const getByPath = (value, path) => {\n if (!path) return value\n const readPath = (target, targetPath) => targetPath.split('.').filter(Boolean).reduce((current, key) => current?.[key], target)\n const directValue = readPath(value, path)\n if (directValue !== undefined) return directValue\n if (path.startsWith('result.')) return readPath(value, path.slice('result.'.length))\n return value?.result === undefined ? undefined : readPath(value.result, path)\n }\n\n const resolveLookupText = (lookup, value) => {\n if (!lookup || value === null || value === undefined || value === \"\") return undefined\n const rows = getByPath(lookup.rows, lookup.path)\n if (!Array.isArray(rows)) return undefined\n const key = String(value)\n const row = rows.find((item) => item && typeof item === \"object\" && String(getByPath(item, lookup.value) ?? \"\") === key)\n if (!row) return undefined\n const text = getByPath(row, lookup.text)\n return text === null || text === undefined || text === \"\" ? undefined : text\n }\n\n const localeCulture = () => {\n const injected = typeof getCulture === \"function\" ? getCulture() : \"\"\n if (injected) return injected\n if (typeof document !== \"undefined\" && document.documentElement.lang) return document.documentElement.lang\n return undefined\n }\n // Patterns come from the culture itself; a fixed day/month/year order would\n // print the same layout in every language.\n const localeDateOptions = { dateStyle: \"short\" }\n const localeTimeOptions = { dateStyle: \"short\", timeStyle: \"short\" }\n const localeIsoPattern = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n const formatLocaleValue = (value) => {\n if (value === null || value === undefined) return \"\"\n const culture = localeCulture()\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) return String(value)\n // Grouping and the decimal separator are the culture's decision; only the\n // precision guard stays, so a long decimal is not rounded away.\n return new Intl.NumberFormat(culture, { maximumFractionDigits: 20 }).format(value)\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? \"\" : new Intl.DateTimeFormat(culture, localeTimeOptions).format(value)\n }\n if (typeof value !== \"string\") return String(value)\n const parts = localeIsoPattern.exec(value.trim())\n if (!parts) return value\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return value\n if (parts[4] === undefined) {\n const day = new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]))\n return new Intl.DateTimeFormat(culture, localeDateOptions).format(day)\n }\n return new Intl.DateTimeFormat(culture, localeTimeOptions).format(parsed)\n }\n\n const readSqlField = (record, field) => {\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return undefined\n if (field in record) return record[field]\n const matched = Object.keys(record).find((key) => key.toLowerCase() === String(field).toLowerCase())\n return matched === undefined ? undefined : record[matched]\n }\n const toSqlRows = (value, collectionPath) => {\n const source = collectionPath ? getByPath(value, collectionPath) : value\n const rows = Array.isArray(source)\n ? source\n : source && typeof source === \"object\"\n ? (Array.isArray(source.items) ? source.items : [source])\n : []\n return rows.filter((item) => item && typeof item === \"object\" && !Array.isArray(item))\n }\n const setSqlField = (record, path, value) => {\n const keys = String(path || \"\").split(\".\").filter(Boolean)\n if (!keys.length) return record\n const next = { ...(record || {}) }\n let target = next\n for (let index = 0; index < keys.length - 1; index += 1) {\n const child = target[keys[index]]\n target[keys[index]] = child && typeof child === \"object\" && !Array.isArray(child) ? { ...child } : {}\n target = target[keys[index]]\n }\n target[keys[keys.length - 1]] = value\n return next\n }\n const bindSqlUrl = (url, record, keyField) => {\n let keyBound = false\n const boundUrl = String(url).replace(/\\{([^}]+)\\}|(?<=\\/):([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, colon) => {\n const name = (braced || colon || \"\").trim()\n if (!name) return match\n const value = readSqlField(record, name) ?? readSqlField(record, keyField)\n if (value === undefined || value === null || value === \"\") return match\n if (name.toLowerCase() === String(keyField).toLowerCase()) keyBound = true\n return encodeURIComponent(String(value))\n })\n return { url: boundUrl, keyBound }\n }\n const appendQueryParam = (url, name, value) =>\n url + (url.includes(\"?\") ? \"&\" : \"?\") + encodeURIComponent(name) + \"=\" + encodeURIComponent(value)\n const hasSqlUrlParams = (url) => /\\{[^}]+\\}|(?<=\\/):[A-Za-z_][A-Za-z0-9_]*/.test(String(url))\n const readUrlKey = (source, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (source === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n return segments.length ? decodeURIComponent(segments[segments.length - 1]) : \"\"\n }\n const filterSqlRowsByKey = (rows, keyField, keyValue) => {\n if (!keyValue) return rows\n const matched = rows.filter((row) => String(readSqlField(row, keyField) ?? \"\") === String(keyValue))\n // No match means the endpoint already filtered server side, or the key column\n // is named differently — keeping the rows beats showing an empty form.\n return matched.length ? matched : rows\n }\n // Update payload: only the columns the user actually edited, plus the key.\n const toSqlChanges = (original, current, keyField) => {\n const changes = {}\n const isSame = (left, right) => JSON.stringify(left ?? null) === JSON.stringify(right ?? null)\n Object.keys(current || {}).forEach((column) => {\n if (!isSame(original ? original[column] : undefined, current[column])) changes[column] = current[column]\n })\n const keyColumn = Object.keys(current || {}).find((column) => column.toLowerCase() === String(keyField).toLowerCase())\n if (keyColumn !== undefined && changes[keyColumn] === undefined) changes[keyColumn] = current[keyColumn]\n return changes\n }\n const callSqlEndpoint = (target, record, keyField, payload) => {\n // The URL is always bound from the full record: a placeholder may reference a\n // column that the update payload does not carry.\n const bound = bindSqlUrl(target.url, record, keyField)\n const request = { url: bound.url, method: target.method }\n if (target.method === \"POST\" || target.method === \"PUT\") request.data = payload === undefined ? record : payload\n const keyValue = readSqlField(record, keyField)\n const hasKey = keyValue !== undefined && keyValue !== null && keyValue !== \"\"\n // The key still has to reach the endpoint when the URL carries no placeholder.\n if (!bound.keyBound && hasKey && target.method !== \"POST\") request.params = { [keyField]: keyValue }\n return apiService.fetchData(request)\n }\n // A new record starts at the top of the form: the first enabled editor takes the\n // cursor, so a Yeni click is immediately followed by typing.\n const focusFirstSqlField = (host) => {\n if (typeof window === \"undefined\") return\n window.requestAnimationFrame(() => {\n const field = host.current?.querySelector(\"input:not([type='hidden']):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])\")\n if (!field) return\n field.focus()\n if (typeof field.select === \"function\") field.select()\n })\n }\n // Defaults declared on the bound children; only a new record starts from them.\n const buildSqlDefaults = (fields) => fields.reduce((record, field) => setSqlField(record, field.path, resolveSqlDefault(field)), {})\n const resolveSqlDefault = (field) => {\n if (field.token !== \"today\" && field.token !== \"now\") return field.value\n const now = new Date()\n const pad = (value) => String(value).padStart(2, \"0\")\n const day = now.getFullYear() + \"-\" + pad(now.getMonth() + 1) + \"-\" + pad(now.getDate())\n if (field.token === \"today\" || field.dateOnly) return day\n return day + \"T\" + pad(now.getHours()) + \":\" + pad(now.getMinutes()) + \":\" + pad(now.getSeconds())\n }\n const toSqlErrorMessage = (error) => error?.response?.data?.error?.message || error?.response?.data?.message || error?.message || translate(\"::App.Platform.IslemBasarisiz\")\n\n // Value of a filter that reads the page URL: a query string parameter, or a\n // path segment — the one after a segment named like the parameter, otherwise\n // the last one, which is the /orders/{id} detail page shape.\n const readDesignerUrlValue = (kind, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (kind === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n if (!segments.length) return \"\"\n const index = name ? segments.findIndex((segment) => segment.toLowerCase() === String(name).toLowerCase()) : -1\n if (index >= 0 && index < segments.length - 1) return decodeURIComponent(segments[index + 1])\n return decodeURIComponent(segments[segments.length - 1])\n }\n // Filters are appended as query parameters: `Column` for an equality and\n // `Column.operator` for anything else, which is what the endpoint parses.\n // Returns \"\" when a required filter has no value yet — the caller must skip the\n // request instead of loading the unfiltered collection.\n const buildFilteredUrl = (url, filters) => {\n const parts = []\n for (const filter of filters || []) {\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return \"\"\n continue\n }\n parts.push(encodeURIComponent(filter.param) + \"=\" + encodeURIComponent(value))\n }\n if (!parts.length) return url\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + parts.join(\"&\")\n }\n\n const buildPlatformFilterParams = (filters) => {\n const expressions = []\n for (const filter of filters || []) {\n if (filter.kind === \"isnull\" || filter.kind === \"notnull\") {\n expressions.push([filter.field, filter.operator, null])\n continue\n }\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return null\n continue\n }\n if (filter.kind === \"in\") {\n const entries = value.split(\",\").map((entry) => entry.trim()).filter(Boolean).map((entry) => [filter.field, \"=\", entry])\n if (!entries.length) continue\n expressions.push(entries.reduce((result, entry) => (result ? [result, \"or\", entry] : entry), null))\n continue\n }\n expressions.push([filter.field, filter.operator, value])\n }\n const params = new URLSearchParams()\n const merged = expressions.reduce((result, expression) => (result ? [result, \"and\", expression] : expression), null)\n if (merged) params.set(\"filter\", JSON.stringify(merged))\n return params\n }\n\n const [data_source_5w_5ibpv, setData_source_5w_5ibpv] = React.useState(null)\n const [data_source_i7_msox4, setData_source_i7_msox4] = React.useState(null)\n const [data_source_0t_avok9] = React.useState(null)\n const [data_source_h3_484qa] = React.useState(null)\n const [data_source_gb_d9cvs] = React.useState(null)\n const [data_source_tq_veb4m, setData_source_tq_veb4m] = React.useState(null)\n const [data_source_d2_bri5s, setData_source_d2_bri5s] = React.useState(null)\n const [data_source_gq_0q1dz, setData_source_gq_0q1dz] = React.useState(null)\n const [data_source_u8_nu1lu, setData_source_u8_nu1lu] = React.useState(null)\n\n const sqlselect_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"GET\", responsePath: \"\" }\n const sqlinsert_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"POST\", responsePath: \"\" }\n const sqlupdate_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"PUT\", responsePath: \"\" }\n const sqldelete_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"DELETE\", responsePath: \"\" }\n const sqlCanSelect_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList\")\n const sqlCanInsert_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Create\")\n const sqlCanUpdate_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Update\")\n const sqlCanDelete_cmp_msyfmegt_jkkn3 = designerHasPermission(\"App.Wizard.RoleList.Delete\")\n const sqlKey_cmp_msyfmegt_jkkn3 = \"id\"\n const sqlKeyParam_cmp_msyfmegt_jkkn3 = \"id\"\n const [data_cmp_msyfmegt_jkkn3, setData_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlOriginal_cmp_msyfmegt_jkkn3, setSqlOriginal_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlMode_cmp_msyfmegt_jkkn3, setSqlMode_cmp_msyfmegt_jkkn3] = React.useState(\"edit\")\n const [sqlIndex_cmp_msyfmegt_jkkn3, setSqlIndex_cmp_msyfmegt_jkkn3] = React.useState(0)\n const [sqlBusy_cmp_msyfmegt_jkkn3, setSqlBusy_cmp_msyfmegt_jkkn3] = React.useState(false)\n const [sqlError_cmp_msyfmegt_jkkn3, setSqlError_cmp_msyfmegt_jkkn3] = React.useState(\"\")\n // Container element, so New can hand the cursor to the first field of the form.\n const sqlHost_cmp_msyfmegt_jkkn3 = React.useRef(null)\n // The Select key can come from the page URL, which is how a detail page reads\n // /api/app/orders/{id} or /api/app/orders?id=… for a single record.\n const sqlUrlKey_cmp_msyfmegt_jkkn3 = readUrlKey(\"query\", sqlKeyParam_cmp_msyfmegt_jkkn3)\n const sqlSelectUrl_cmp_msyfmegt_jkkn3 = React.useMemo(() => {\n if (!sqlselect_cmp_msyfmegt_jkkn3) return \"\"\n const base = sqlUrlKey_cmp_msyfmegt_jkkn3\n ? (() => {\n const bound = bindSqlUrl(sqlselect_cmp_msyfmegt_jkkn3.url, { [sqlKeyParam_cmp_msyfmegt_jkkn3]: sqlUrlKey_cmp_msyfmegt_jkkn3 }, sqlKeyParam_cmp_msyfmegt_jkkn3)\n return bound.keyBound ? bound.url : appendQueryParam(bound.url, sqlKeyParam_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3)\n })()\n : sqlselect_cmp_msyfmegt_jkkn3.url\n return base\n }, [sqlUrlKey_cmp_msyfmegt_jkkn3])\n // A key in the page URL always narrows the result: a list endpoint ignores the\n // parameter server side, so the requested record is picked out here. Without a\n // key every row is kept and the navigation below takes over.\n const sqlRows_cmp_msyfmegt_jkkn3 = React.useMemo(\n () => filterSqlRowsByKey(toSqlRows(data_source_5w_5ibpv, \"\"), sqlKey_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3),\n [data_source_5w_5ibpv, sqlUrlKey_cmp_msyfmegt_jkkn3],\n )\n // A fresh result set resets the position; New mode is left untouched so an\n // unsaved draft is not overwritten by a re-render.\n React.useEffect(() => {\n setSqlIndex_cmp_msyfmegt_jkkn3((current) => (current < sqlRows_cmp_msyfmegt_jkkn3.length ? current : 0))\n }, [sqlRows_cmp_msyfmegt_jkkn3])\n React.useEffect(() => {\n const row = sqlRows_cmp_msyfmegt_jkkn3[sqlIndex_cmp_msyfmegt_jkkn3]\n if (!row) return\n setData_cmp_msyfmegt_jkkn3(row)\n setSqlOriginal_cmp_msyfmegt_jkkn3(row)\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n }, [sqlRows_cmp_msyfmegt_jkkn3, sqlIndex_cmp_msyfmegt_jkkn3])\n const sqlHasKey_cmp_msyfmegt_jkkn3 = (() => {\n const value = readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n return value !== undefined && value !== null && value !== \"\"\n })()\n const sqlSetField_cmp_msyfmegt_jkkn3 = React.useCallback((path, value) => setData_cmp_msyfmegt_jkkn3((current) => setSqlField(current, path, value)), [])\n const sqlReload_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n // Without the Read permission the container is not rendered at all; the\n // request is dropped here so it is not fired for a form nobody can see.\n if (!sqlCanSelect_cmp_msyfmegt_jkkn3) return\n if (!sqlSelectUrl_cmp_msyfmegt_jkkn3) return\n // Calling a URL that still holds a placeholder is a guaranteed 400, so the\n // missing key is reported in the component instead.\n if (hasSqlUrlParams(sqlSelectUrl_cmp_msyfmegt_jkkn3)) {\n throw new Error(translate(\"::App.Form.MissingKeyParam\", { 0: sqlKeyParam_cmp_msyfmegt_jkkn3 }))\n }\n const response = await apiService.fetchData({ url: sqlSelectUrl_cmp_msyfmegt_jkkn3, method: \"GET\" })\n setData_source_5w_5ibpv(getByPath(response.data, sqlselect_cmp_msyfmegt_jkkn3.responsePath))\n }, [sqlSelectUrl_cmp_msyfmegt_jkkn3])\n // Reload wrapped with the busy/error handling the toolbar and mount effect need,\n // so a failing Select is reported in the component instead of the console.\n const sqlRefresh_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }, [sqlReload_cmp_msyfmegt_jkkn3])\n const sqlNew_cmp_msyfmegt_jkkn3 = React.useCallback(() => {\n if (!sqlCanInsert_cmp_msyfmegt_jkkn3) return\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n // Defaults are resolved on every New, so an @today column carries the day the\n // record was opened rather than the day the component was generated.\n const sqlDraft_cmp_msyfmegt_jkkn3 = buildSqlDefaults([{\"path\":\"IsDefault\",\"value\":true},{\"path\":\"IsStatic\",\"value\":true},{\"path\":\"IsPublic\",\"value\":false},{\"path\":\"EntityVersion\",\"value\":3},{\"path\":\"ExtraProperties\",\"value\":\"{}\"}])\n setData_cmp_msyfmegt_jkkn3(sqlDraft_cmp_msyfmegt_jkkn3)\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n // After the re-render that paints the empty form, not before it.\n focusFirstSqlField(sqlHost_cmp_msyfmegt_jkkn3)\n }, [])\n const sqlPrev_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.max(0, current - 1)), [])\n const sqlNext_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.min(sqlRows_cmp_msyfmegt_jkkn3.length - 1, current + 1)), [sqlRows_cmp_msyfmegt_jkkn3])\n const sqlSave_cmp_msyfmegt_jkkn3 = async () => {\n const isNew = sqlMode_cmp_msyfmegt_jkkn3 === \"new\"\n const target = isNew ? sqlinsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3\n // The button is already hidden without the permission; a script calling save\n // through a ref must not be able to walk around it either.\n if (!(isNew ? sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlCanUpdate_cmp_msyfmegt_jkkn3)) return\n if (!target) {\n setSqlError_cmp_msyfmegt_jkkn3(isNew ? translate(\"::App.Form.InsertEndpointMissing\") : translate(\"::App.Form.UpdateEndpointMissing\"))\n return\n }\n // Update carries only the edited columns plus the key; Insert sends the record.\n const payload = isNew ? data_cmp_msyfmegt_jkkn3 : toSqlChanges(sqlOriginal_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n if (!isNew && Object.keys(payload).filter((column) => column.toLowerCase() !== String(sqlKey_cmp_msyfmegt_jkkn3).toLowerCase()).length === 0) {\n setSqlError_cmp_msyfmegt_jkkn3(translate(\"::App.Form.NoChanges\"))\n return\n }\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n const response = await callSqlEndpoint(target, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3, payload)\n // With a Select endpoint the reload is the source of truth and its effect\n // repopulates the form; without one, the response row is all there is.\n if (sqlselect_cmp_msyfmegt_jkkn3) {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } else {\n const saved = toSqlRows(response?.data, \"\")[0]\n if (saved) {\n setData_cmp_msyfmegt_jkkn3(saved)\n setSqlOriginal_cmp_msyfmegt_jkkn3(saved)\n }\n }\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n const sqlDelete_cmp_msyfmegt_jkkn3 = async () => {\n if (!sqlCanDelete_cmp_msyfmegt_jkkn3 || !sqldelete_cmp_msyfmegt_jkkn3 || !sqlHasKey_cmp_msyfmegt_jkkn3 || sqlMode_cmp_msyfmegt_jkkn3 === \"new\") return\n // Deleting cannot be undone from the form, so it always asks first.\n const confirmMessage = translate(\"::App.Form.DeleteConfirm\", { 0: sqlKey_cmp_msyfmegt_jkkn3 + \" = \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) })\n if (typeof window !== \"undefined\" && !window.confirm(confirmMessage)) return\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await callSqlEndpoint(sqldelete_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n setData_cmp_msyfmegt_jkkn3({})\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n React.useEffect(() => { void sqlRefresh_cmp_msyfmegt_jkkn3() }, [sqlRefresh_cmp_msyfmegt_jkkn3])\n\n const filters_source_tq_veb4m = [{ param: \"RoleId\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"id\"), required: true }]\n const filterKey_source_tq_veb4m = JSON.stringify(filters_source_tq_veb4m)\n React.useEffect(() => {\n const url = buildFilteredUrl(\"/api/app/crudendpoint/AbpUserRoles\", filters_source_tq_veb4m)\n // An unmet required filter leaves the component empty on purpose.\n if (!url) {\n setData_source_tq_veb4m(null)\n return\n }\n let active = true\n apiService.fetchData({ url, method: 'GET' }).then((response) => {\n if (active) setData_source_tq_veb4m(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUserRoles · GetList endpoint error:\", error))\n return () => { active = false }\n }, [filterKey_source_tq_veb4m])\n\n React.useEffect(() => {\n let active = true\n apiService.fetchData({ url: \"/api/app/crudendpoint/AbpUsers\", method: 'GET' }).then((response) => {\n if (active) setData_source_gq_0q1dz(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUsers · GetList endpoint error:\", error))\n return () => { active = false }\n }, [])\n\n const platformFilters_cmp_mt0288i3_ebpj5 = [{ field: \"Id\", operator: \"=\", kind: \"eq\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"Id\"), required: true }]\n const platformKey_cmp_mt0288i3_ebpj5 = JSON.stringify(platformFilters_cmp_mt0288i3_ebpj5)\n // The views keep the params object in their own state and write back to it, so\n // it must stay the same object until a filter value actually changes.\n const platformParams_cmp_mt0288i3_ebpj5 = React.useMemo(() => buildPlatformFilterParams(platformFilters_cmp_mt0288i3_ebpj5), [platformKey_cmp_mt0288i3_ebpj5])\n\n const handle_cmp_msyfpfia_ifhdq_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"Name\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjl707_crcn7_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"NormalizedName\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjdodn_u3o74_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ConcurrencyStamp\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyje9ys_2c08a_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsDefault\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjebu2_27tqi_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsStatic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjmetn_gojol_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsPublic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjo0g8_oathk_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"EntityVersion\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjplfq_py8lz_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ExtraProperties\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n return (\n <>\n {sqlCanSelect_cmp_msyfmegt_jkkn3 ? (\n {sqlHasKey_cmp_msyfmegt_jkkn3 ? sqlKey_cmp_msyfmegt_jkkn3 + \": \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) : sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : \"\"}}>\n \n
\n \n \n \n \n {\"Is Default\"}\n \n \n {\"Is Static\"}\n \n \n {\"Is Public\"}\n \n \n \n
\n
\n {sqlRows_cmp_msyfmegt_jkkn3.length > 1 ? (\n <>\n {translate(\"::App.Platform.Previous\")} \n {`${sqlIndex_cmp_msyfmegt_jkkn3 + 1} / ${sqlRows_cmp_msyfmegt_jkkn3.length}`} \n = sqlRows_cmp_msyfmegt_jkkn3.length - 1} onClick={sqlNext_cmp_msyfmegt_jkkn3}>{translate(\"::App.Platform.Next\")} \n \n >\n ) : null}\n {sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 ? {translate(\"::App.Platform.New\")} : null}\n {(sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3 && sqlCanUpdate_cmp_msyfmegt_jkkn3) ? { void sqlSave_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Save\")} : null}\n {sqldelete_cmp_msyfmegt_jkkn3 && sqlCanDelete_cmp_msyfmegt_jkkn3 ? { void sqlDelete_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Delete\")} : null}\n {sqlselect_cmp_msyfmegt_jkkn3 && sqlCanSelect_cmp_msyfmegt_jkkn3 ? { void sqlRefresh_cmp_msyfmegt_jkkn3() }}>{translate(\"::App.Platform.Refresh\")} : null}\n {sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : translate(\"::App.Platform.Editing\")} \n
\n {sqlError_cmp_msyfmegt_jkkn3 ?
{sqlError_cmp_msyfmegt_jkkn3}
: null}\n
\n \n ) : null}\n \n {(() => {\n const gridItems_cmp_mt023r5u_rb2jv = (Array.isArray(getByPath(data_source_tq_veb4m, \"\")) ? getByPath(data_source_tq_veb4m, \"\") : Array.isArray(data_source_tq_veb4m) ? data_source_tq_veb4m : null)\n // Nothing loaded yet — an endpoint still in flight, or a required filter\n // holding the request back — is an empty grid, not a binding error.\n if (gridItems_cmp_mt023r5u_rb2jv === null || gridItems_cmp_mt023r5u_rb2jv === undefined) return null\n if (!Array.isArray(gridItems_cmp_mt023r5u_rb2jv)) return
Grid items bağlantısı bir koleksiyon döndürmelidir.
\n const gridColumns_cmp_mt023r5u_rb2jv = [\"UserId\"]\n const gridLookups_cmp_mt023r5u_rb2jv = { \"UserId\": { rows: data_source_gq_0q1dz, path: \"\", value: \"Id\", text: \"UserName\" } }\n if (!gridColumns_cmp_mt023r5u_rb2jv.length) return
Preview için en az bir sütun seçin.
\n return (\n
\n
\n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => {(column === \"value\" ? \"Value\" : column)} )}\n \n \n {gridItems_cmp_mt023r5u_rb2jv.map((gridItem_cmp_mt023r5u_rb2jv, rowIndex) => (\n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => {\n const cellValue_cmp_mt023r5u_rb2jv = column === \"value\" ? gridItem_cmp_mt023r5u_rb2jv : getByPath(gridItem_cmp_mt023r5u_rb2jv, column)\n const lookedUp = resolveLookupText(gridLookups_cmp_mt023r5u_rb2jv[column], cellValue_cmp_mt023r5u_rb2jv)\n const cellText = lookedUp === undefined ? cellValue_cmp_mt023r5u_rb2jv : lookedUp\n const text = typeof cellText === \"object\" && cellText !== null && !(cellText instanceof Date) ? JSON.stringify(cellText) : formatLocaleValue(cellText) || \"—\"\n return {text} \n })}\n \n ))}\n \n
\n
\n )\n })()}\n
\n {platformParams_cmp_mt0288i3_ebpj5 === null ? null : (\n \n )}\n >\n )\n}\n\nexport default RoleComponent",
+ "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[{\"id\":\"cmp_msyfmegt_jkkn3\",\"type\":\"Form\",\"kind\":\"layout\",\"props\":{\"selectEndpoint\":\"source_5w_5ibpv\",\"insertEndpoint\":\"source_h3_484qa\",\"updateEndpoint\":\"source_gb_d9cvs\",\"deleteEndpoint\":\"source_0t_avok9\",\"keyFieldName\":\"id\",\"collectionPath\":\"\",\"keySource\":\"query\",\"keyParamName\":\"id\",\"previewKeyValue\":\"\",\"autoLoad\":true,\"showToolbar\":true,\"gap\":16,\"className\":\"\",\"title\":\"::AbpIdentity.Roles\",\"columnCount\":2,\"selectPermission\":\"\",\"insertPermission\":\"\"},\"events\":{\"onLoad\":\"\",\"onRecordChange\":\"\",\"onFieldChange\":\"\",\"onNewRecord\":\"\",\"onModeChange\":\"\",\"onBeforeSave\":\"\",\"onAfterSave\":\"\",\"onBeforeDelete\":\"\",\"onAfterDelete\":\"\",\"onError\":\"\"},\"bindings\":{},\"children\":[{\"id\":\"cmp_msyfpfia_ifhdq\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"::App.Platform.Name\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"Name\"}},\"children\":[],\"ref\":\"input1\"},{\"id\":\"cmp_msyjl707_crcn7\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Normalized Name :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"NormalizedName\"}},\"children\":[],\"ref\":\"input2\"},{\"id\":\"cmp_msyjdodn_u3o74\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Concurrent Stamp : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ConcurrencyStamp\"}},\"children\":[],\"ref\":\"input3\"},{\"id\":\"cmp_msyje9ys_2c08a\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Default\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsDefault\"}},\"children\":[],\"ref\":\"checkbox1\"},{\"id\":\"cmp_msyjebu2_27tqi\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Static\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsStatic\"}},\"children\":[],\"ref\":\"checkbox2\"},{\"id\":\"cmp_msyjmetn_gojol\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Public\",\"style\":{},\"sqlDefaultValue\":\"false\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsPublic\"}},\"children\":[],\"ref\":\"checkbox3\"},{\"id\":\"cmp_msyjo0g8_oathk\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Entity Version : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"number\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"3\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"EntityVersion\"}},\"children\":[],\"ref\":\"input4\"},{\"id\":\"cmp_msyjplfq_py8lz\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Extra Properties :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"{}\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ExtraProperties\"}},\"children\":[],\"ref\":\"input5\"}],\"ref\":\"form1\"},{\"id\":\"cmp_mt023r5u_rb2jv\",\"type\":\"Grid\",\"kind\":\"ui\",\"props\":{\"hoverable\":true,\"overflow\":true,\"borderlessRow\":false,\"compact\":false,\"cols\":\"3\",\"rows\":\"2\",\"gap\":\"4\",\"colGap\":\"4\",\"rowGap\":\"4\",\"responsive\":true,\"smCols\":\"1\",\"mdCols\":\"2\",\"lgCols\":\"3\",\"xlCols\":\"4\",\"autoFit\":false,\"minColWidth\":\"200px\",\"height\":\"auto\",\"className\":\"mt-4\",\"items\":[{\"id\":\"grid-item-1\",\"content\":\"Grid Item 1\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":1,\"rowStart\":1,\"className\":\"bg-blue-100 p-4 border rounded\"},{\"id\":\"grid-item-2\",\"content\":\"Grid Item 2\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":2,\"rowStart\":1,\"className\":\"bg-green-100 p-4 border rounded\"},{\"id\":\"grid-item-3\",\"content\":\"Grid Item 3\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":1,\"className\":\"bg-yellow-100 p-4 border rounded\"},{\"id\":\"grid-item-4\",\"content\":\"Grid Item 4\",\"colSpan\":2,\"rowSpan\":1,\"colStart\":1,\"rowStart\":2,\"className\":\"bg-purple-100 p-4 border rounded\"},{\"id\":\"grid-item-5\",\"content\":\"Grid Item 5\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":2,\"className\":\"bg-red-100 p-4 border rounded\"}],\"dataColumns\":[\"UserId\"],\"columnLookups\":{\"UserId\":{\"sourceId\":\"source_gq_0q1dz\",\"path\":\"\",\"valueField\":\"Id\",\"textField\":\"UserName\"}},\"columnCaptions\":{}},\"events\":{},\"bindings\":{\"items\":{\"sourceId\":\"source_tq_veb4m\",\"path\":\"\"}},\"children\":[],\"ref\":\"grid1\"},{\"id\":\"cmp_mt0288i3_ebpj5\",\"type\":\"ListView\",\"kind\":\"platform\",\"props\":{\"listFormCode\":\"AbpIdentity.Roles\",\"height\":\"520px\",\"filters\":[{\"id\":\"filter_y9_tr73w\",\"field\":\"Id\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.Id\",\"previewValue\":\"\",\"required\":true}],\"className\":\"mt-4\"},\"events\":{},\"bindings\":{},\"children\":[],\"ref\":\"listView1\"}],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[{\"id\":\"source_5w_5ibpv\",\"name\":\"AbpRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_i7_msox4\",\"name\":\"AbpRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_0t_avok9\",\"name\":\"AbpRoles · Delete\",\"method\":\"DELETE\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_h3_484qa\",\"name\":\"AbpRoles · Create\",\"method\":\"POST\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_gb_d9cvs\",\"name\":\"AbpRoles · Update\",\"method\":\"PUT\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_tq_veb4m\",\"name\":\"AbpUserRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles\",\"responsePath\":\"\",\"filters\":[{\"id\":\"filter_sw_86e0q\",\"field\":\"RoleId\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.id\",\"previewValue\":\"\",\"required\":true}]},{\"id\":\"source_d2_bri5s\",\"name\":\"AbpUserRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_gq_0q1dz\",\"name\":\"AbpUsers · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers\",\"responsePath\":\"\"},{\"id\":\"source_u8_nu1lu\",\"name\":\"AbpUsers · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers/{id}\",\"responsePath\":\"\"}],\"permissionCode\":\"App.Wizard.RoleList\"}}",
"Description": "",
"IsActive": true,
"Dependencies": [],
diff --git a/ui/src/components/common/InfoSection.tsx b/ui/src/components/common/InfoSection.tsx
deleted file mode 100644
index 04d726dc..00000000
--- a/ui/src/components/common/InfoSection.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from "react";
-import classNames from "classnames";
-
-interface InfoSectionProps {
- title: string;
- children: React.ReactNode;
-}
-
-export const InfoSection: React.FC = ({
- title,
- children,
-}) => (
-
-
- {title}
-
-
{children}
-
-);
-
-interface InfoItemProps {
- label: string;
- value: string | number;
- isMono?: boolean;
- isBold?: boolean;
- isBadge?: boolean;
- badgeColor?: string;
-}
-
-export const InfoItem: React.FC = ({
- label,
- value,
- isMono,
- isBold,
- isBadge,
- badgeColor,
-}) => (
-
-
{label}
- {isBadge ? (
-
-
- {value}
-
-
- ) : (
-
- {value}
-
- )}
-
-);
diff --git a/ui/src/components/common/LoadingSpinner.tsx b/ui/src/components/common/LoadingSpinner.tsx
deleted file mode 100644
index 80d8ccc7..00000000
--- a/ui/src/components/common/LoadingSpinner.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-
-interface LoadingSpinnerProps {
- size?: 'sm' | 'md' | 'lg';
- color?: 'blue' | 'white' | 'gray';
- text?: string;
-}
-
-const LoadingSpinner: React.FC = ({
- size = 'md',
- color = 'blue',
- text
-}) => {
- const sizeClasses = {
- sm: 'h-4 w-4',
- md: 'h-6 w-6',
- lg: 'h-8 w-8'
- };
-
- const colorClasses = {
- blue: 'border-blue-600',
- white: 'border-white',
- gray: 'border-gray-600'
- };
-
- return (
-
- );
-};
-
-export default LoadingSpinner;
\ No newline at end of file
diff --git a/ui/src/components/common/ModuleHeader.tsx b/ui/src/components/common/ModuleHeader.tsx
deleted file mode 100644
index dc23c191..00000000
--- a/ui/src/components/common/ModuleHeader.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from "react";
-
-interface ModuleHeaderProps {
- title: string;
- breadcrumbs?: Array<{
- name: string;
- href?: string;
- }>;
-}
-
-const ModuleHeader: React.FC = ({
- title,
- breadcrumbs = [],
-}) => {
- return (
-
- {/* Breadcrumb Navigation */}
-
-
-
-
-
-
- {breadcrumbs.map((breadcrumb, index) => (
-
-
- /
-
- {breadcrumb.name}
-
-
-
- ))}
-
-
-
- /
-
- {title}
-
-
-
-
-
-
- );
-};
-
-export default ModuleHeader;
diff --git a/ui/src/components/common/StatusBadge.tsx b/ui/src/components/common/StatusBadge.tsx
deleted file mode 100644
index d0683812..00000000
--- a/ui/src/components/common/StatusBadge.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react'
-import classNames from 'classnames'
-import { FaCheckCircle, FaExclamationTriangle, FaClock, FaTimes } from 'react-icons/fa'
-
-interface StatusBadgeProps {
- status: string
- size?: 'sm' | 'md'
- showIcon?: boolean
-}
-
-const StatusBadge: React.FC = ({ status, size = 'md', showIcon = true }) => {
- const sizeClasses = {
- sm: 'px-1.5 py-0.5 text-xs',
- md: 'px-2 py-0.5 text-xs',
- }
-
- const getStatusConfig = (status: string) => {
- switch (status) {
- case 'active':
- return {
- color: 'bg-green-100 text-green-800',
- icon: ,
- text: 'Aktif',
- }
- case 'inactive':
- return {
- color: 'bg-red-100 text-red-800',
- icon: ,
- text: 'Pasif',
- }
- case 'pending':
- return {
- color: 'bg-yellow-100 text-yellow-800',
- icon: ,
- text: 'Beklemede',
- }
- case 'critical':
- return {
- color: 'bg-red-100 text-red-800',
- icon: ,
- text: 'Kritik',
- }
- case 'low':
- return {
- color: 'bg-yellow-100 text-yellow-800',
- icon: ,
- text: 'App.ListFormTodoBoard.PriorityLow',
- }
- case 'normal':
- return {
- color: 'bg-green-100 text-green-800',
- icon: ,
- text: 'Normal',
- }
- default:
- return {
- color: 'bg-gray-100 text-gray-800',
- icon: ,
- text: 'Bilinmiyor',
- }
- }
- }
-
- const config = getStatusConfig(status)
-
- return (
-
- {showIcon && {config.icon} }
- {config.text}
-
- )
-}
-
-export default StatusBadge
diff --git a/ui/src/components/common/WidgetGroup.tsx b/ui/src/components/common/WidgetGroup.tsx
deleted file mode 100644
index eee6275b..00000000
--- a/ui/src/components/common/WidgetGroup.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import classNames from "classnames";
-import Widget, { type colorType } from "./Widget";
-import { WidgetEditDto, WidgetGroupDto } from "../../types/common";
-
-export default function WidgetGroup({
- widgetGroups,
-}: {
- widgetGroups: WidgetGroupDto[];
-}) {
- return (
-
- {widgetGroups.map((group, gIdx) => (
-
- {group.items.map((item: WidgetEditDto, order: number) => (
-
- {
- if (item.onClick) {
- // eslint-disable-next-line no-eval
- eval(item.onClick);
- }
- }}
- />
-
- ))}
-
- ))}
-
- );
-}
diff --git a/ui/src/components/componentEditor/ComponentPreview.tsx b/ui/src/components/componentEditor/ComponentPreview.tsx
deleted file mode 100644
index b60813e5..00000000
--- a/ui/src/components/componentEditor/ComponentPreview.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React, { useMemo } from 'react'
-import DynamicRenderer from './DynamicRenderer'
-import { useComponents } from '@/contexts/ComponentContext'
-import { parseComponentDependencies } from '@/contexts/componentRuntime'
-import { Loading } from '../shared'
-import { useLocalization } from '@/utils/hooks/useLocalization'
-
-export interface ComponentPreviewProps {
- componentName?: string
- className?: string
-}
-
-const ComponentPreview: React.FC = ({ componentName, className = '' }) => {
- const { translate } = useLocalization()
- const { components, loading } = useComponents()
-
- // Referans olarak sabit tutulmalı: DynamicRenderer bunu effect bağımlılığı olarak kullanıyor.
- const dependencies = useMemo(() => {
- const component = components?.find((item) => item.name === componentName && item.isActive)
- return parseComponentDependencies(component?.dependencies)
- }, [components, componentName])
-
- if (!componentName) {
- return (
-
- {translate('::App.DeveloperKitComponentPreview.NoComponentName')}
-
- )
- }
-
- if (loading || !Array.isArray(components)) {
- return (
-
- )
- }
-
- return (
-
-
-
- )
-}
-
-export default ComponentPreview
diff --git a/ui/src/components/componentEditor/DynamicRenderer.tsx b/ui/src/components/componentEditor/DynamicRenderer.tsx
deleted file mode 100644
index fcc3f4f4..00000000
--- a/ui/src/components/componentEditor/DynamicRenderer.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-import React, { useCallback, useEffect, useRef, useState } from 'react'
-import axios from 'axios'
-import DOMPurify from 'dompurify'
-import apiService from '@/services/api.service'
-import * as UiKit from '@/components/ui'
-import {
- getComponentRuntimeCode,
- parseComponentDependencies,
- toErrorMessage,
- type BabelLike,
-} from '@/contexts/componentRuntime'
-import { useLocalization } from '@/utils/hooks/useLocalization'
-import { usePermission } from '@/utils/hooks/usePermission'
-import { useComponents } from '../../contexts/ComponentContext'
-import ErrorBoundary from './ErrorBoundary'
-import PlatformIcon from './PlatformIcon'
-import PlatformSelectComponent from './selectComponents'
-import PlatformViewHost from './PlatformViewHost'
-import { toast } from '../ui'
-import { Loading } from '../shared'
-
-type RuntimeModule = { exports: { default?: React.ComponentType } }
-
-const compileComponent = (
- code: string,
- scope: Record,
- Babel: BabelLike,
-): React.ComponentType | undefined => {
- const transpiled = Babel.transform(code, {
- filename: 'component.tsx',
- presets: ['typescript', 'react'],
- plugins: ['transform-modules-commonjs'],
- }).code
-
- if (!transpiled) throw new Error('Boş derleme çıktısı')
-
- const module: RuntimeModule = { exports: {} }
- const require = (moduleName: string) => {
- if (moduleName === 'react') return React
- if (moduleName === 'axios') return axios
- if (moduleName === 'dompurify') return DOMPurify
- if (moduleName === '@/services/api.service') return apiService
- throw new Error(`Modül bulunamadı: ${moduleName}`)
- }
-
- const scopedEval = new Function('module', 'exports', 'require', ...Object.keys(scope), transpiled)
- scopedEval(module, module.exports, require, ...Object.values(scope))
-
- return module.exports.default
-}
-
-// The UI kit is spread as-is: every exported component is available to runtime
-// code without an extra per-component entry here.
-const staticComponents: Record = {
- ...UiKit,
- // Icon props travel as names, so generated code needs the same name resolver
- // the designer canvas draws with.
- PlatformIcon,
- // Select.componentAs is stored as a name, so generated code needs the same
- // resolver the designer canvas renders with.
- PlatformSelectComponent,
- PlatformViewHost,
- toast,
- apiService,
- DOMPurify,
- UiKit,
-}
-
-interface DynamicRendererProps {
- componentName: string
- dependencies?: string[]
-}
-
-const DynamicRenderer: React.FC = ({
- componentName,
- dependencies: externalDeps,
-}) => {
- const [Component, setComponent] = useState | null>(null)
- const [error, setError] = useState(null)
- const { getComponentByName, components } = useComponents()
- const { checkPermission } = usePermission()
- const { translate } = useLocalization()
-
- // Read through refs so the granted policies and the active language can change
- // without recompiling the component; generated code calls both on every render.
- const checkPermissionRef = useRef(checkPermission)
- checkPermissionRef.current = checkPermission
- const stableCheckPermission = useCallback(
- (permission?: string) => checkPermissionRef.current(permission),
- [],
- )
- const translateRef = useRef(translate)
- translateRef.current = translate
- const stableTranslate = useCallback(
- (...args: Parameters) => translateRef.current(...args),
- [],
- )
-
- useEffect(() => {
- let cancelled = false
- setComponent(null)
- setError(null)
-
- const storedComponent = getComponentByName(componentName)
- if (!storedComponent) {
- setError(`Component ${componentName} not found`)
- return
- }
-
- const build = async () => {
- // Babel is several megabytes; load it only when something must be compiled.
- const Babel = (await import('@babel/standalone')) as unknown as BabelLike
- if (cancelled) return
-
- const map = new Map(
- components.map((component) => [
- component.name,
- {
- code: getComponentRuntimeCode(component),
- dependencies: parseComponentDependencies(component.dependencies),
- },
- ]),
- )
-
- const compiled: Record = {}
-
- const compileWithDependencies = (name: string): any => {
- if (compiled[name]) return compiled[name]
-
- const entry = map.get(name)
- if (!entry) {
- if (staticComponents[name]) {
- compiled[name] = staticComponents[name]
- return staticComponents[name]
- }
- throw new Error(`Component ${name} not found`)
- }
-
- const depNames = name === componentName && externalDeps ? externalDeps : entry.dependencies
-
- const deps: Record = {}
- for (const dep of depNames) {
- deps[dep] = compileWithDependencies(dep)
- }
-
- const component = compileComponent(
- entry.code,
- {
- React,
- ...staticComponents,
- checkPermission: stableCheckPermission,
- translate: stableTranslate,
- ...deps,
- },
- Babel,
- )
- if (!component) throw new Error(`Component ${name} bir default export döndürmüyor`)
-
- compiled[name] = component
- return component
- }
-
- try {
- const RootComponent = compileWithDependencies(componentName)
- if (!cancelled) setComponent(() => RootComponent)
- } catch (err) {
- console.error('Compilation error:', err)
- if (!cancelled) setError(toErrorMessage(err))
- }
- }
-
- void build()
-
- return () => {
- cancelled = true
- }
- }, [
- componentName,
- externalDeps,
- components,
- getComponentByName,
- stableCheckPermission,
- stableTranslate,
- ])
-
- if (error) {
- return (
-
-
{componentName}
-
{error}
-
- )
- }
-
- if (!Component)
- return (
-
- )
-
- return (
-
-
-
- )
-}
-
-export default DynamicRenderer
diff --git a/ui/src/components/layouts/AuthLayout/Cover.tsx b/ui/src/components/layouts/AuthLayout/Cover.tsx
deleted file mode 100644
index 6e4de833..00000000
--- a/ui/src/components/layouts/AuthLayout/Cover.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { cloneElement } from 'react'
-import Logo from '@/components/template/Logo'
-import { APP_NAME } from '@/constants/app.constant'
-import type { CommonProps } from '@/proxy/common'
-import type { ReactNode, ReactElement } from 'react'
-import { Avatar } from '@/components/ui'
-import { useLocalization } from '@/utils/hooks/useLocalization'
-import { ROUTES_ENUM } from '@/routes/route.constant'
-import useDarkMode from '@/utils/hooks/useDarkmode'
-
-interface CoverProps extends CommonProps {
- content?: ReactNode
-}
-
-const Cover = ({ children, content, ...rest }: CoverProps) => {
- const { translate } = useLocalization()
- const [isDarkMode] = useDarkMode()
-
- return (
-
-
- {/* Koyulaştırıcı katman */}
-
-
-
-
-
-
-
-
-
Sedat ÖZTÜRK
-
{translate('::App.Listform.ListformField.Founder')}
-
-
-
{translate('::App.LoginPanel.Message')}
-
-
- Copyright © {`${new Date().getFullYear()}`}{' '}
- {`${APP_NAME}`} {' '}
-
-
-
-
-
-
-
{content}
- {children ? cloneElement(children as ReactElement, { ...rest }) : null}
-
-
-
- )
-}
-
-
-export default Cover
diff --git a/ui/src/components/layouts/AuthLayout/Side.tsx b/ui/src/components/layouts/AuthLayout/Side.tsx
deleted file mode 100644
index a1f71445..00000000
--- a/ui/src/components/layouts/AuthLayout/Side.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React, { cloneElement } from 'react'
-import Avatar from '@/components/ui/Avatar'
-import Logo from '@/components/template/Logo'
-import { APP_NAME } from '@/constants/app.constant'
-import type { CommonProps } from '@/proxy/common'
-import { useLocalization } from '@/utils/hooks/useLocalization'
-import { ROUTES_ENUM } from '@/routes/route.constant'
-import useDarkMode from '@/utils/hooks/useDarkmode'
-
-interface SideProps extends CommonProps {
- content?: React.ReactNode
-}
-
-const Side = ({ children, content, ...rest }: SideProps) => {
- const { translate } = useLocalization()
- const [isDarkMode] = useDarkMode()
-
- return (
-
-
-
-
-
-
-
-
-
-
Sedat ÖZTÜRK
-
{ translate('::App.Listform.ListformField.Founder')}
-
-
-
- { translate('::App.LoginPanel.Message')}
-
-
-
- Copyright © {`${new Date().getFullYear()}`}{' '}
- {`${APP_NAME}`} {' '}
-
-
-
-
-
-
{content}
- {children
- ? cloneElement(children as React.ReactElement, {
- ...rest,
- })
- : null}
-
-
-
- )
-}
-
-export default Side
diff --git a/ui/src/components/route/AppRoute.tsx b/ui/src/components/route/AppRoute.tsx
deleted file mode 100644
index d2f7e348..00000000
--- a/ui/src/components/route/AppRoute.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { useEffect, useCallback } from 'react'
-import { useStoreState, useStoreActions } from '@/store'
-import { useLocation } from 'react-router-dom'
-import type { LayoutType } from '@/proxy/theme/models'
-import type { ComponentType } from 'react'
-
-export type AppRouteProps = {
- component: ComponentType
- routeKey: string
- layout?: LayoutType
-}
-
-const AppRoute = >({
- component: Component,
- routeKey,
- ...props
-}: AppRouteProps) => {
- const location = useLocation()
-
- const { setCurrentRouteKey } = useStoreActions((actions) => actions.base.common)
- const { setLayout, setPreviousLayout } = useStoreActions((actions) => actions.theme)
-
- const layoutType = useStoreState((state) => state.theme.layout.type)
- const previousLayout = useStoreState((state) => state.theme.layout.previousType)
-
- const handleLayoutChange = useCallback(() => {
- setCurrentRouteKey(routeKey)
-
- if (props.layout && props.layout !== layoutType) {
- setPreviousLayout(layoutType)
- setLayout(props.layout)
- }
-
- if (!props.layout && previousLayout && layoutType !== previousLayout) {
- setLayout(previousLayout)
- setPreviousLayout('')
- }
- }, [
- layoutType,
- previousLayout,
- props.layout,
- routeKey,
- setCurrentRouteKey,
- setLayout,
- setPreviousLayout,
- ])
-
- useEffect(() => {
- handleLayoutChange()
- }, [location, handleLayoutChange])
-
- return
-}
-
-export default AppRoute
diff --git a/ui/src/components/route/AuthorityGuard.tsx b/ui/src/components/route/AuthorityGuard.tsx
deleted file mode 100644
index 983ebb51..00000000
--- a/ui/src/components/route/AuthorityGuard.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-// AuthorityGuard.tsx
-import { PropsWithChildren } from 'react'
-import { Navigate, useLocation } from 'react-router-dom'
-import useAuthority from '@/utils/hooks/useAuthority'
-import { getAccessDeniedPath } from '@/utils/routing'
-
-type AuthorityGuardProps = PropsWithChildren<{
- userAuthority?: string[]
- authority?: string[]
-}>
-
-const AuthorityGuard = (props: AuthorityGuardProps) => {
- const { userAuthority = [], authority = [], children } = props
- const roleMatched = useAuthority(userAuthority, authority)
- const location = useLocation()
-
- if (!roleMatched) {
- const to = getAccessDeniedPath(location.pathname)
- return
- }
-
- return <>{children}>
-}
-
-export default AuthorityGuard
diff --git a/ui/src/components/route/PublicRoute.tsx b/ui/src/components/route/PublicRoute.tsx
deleted file mode 100644
index ccf1b44a..00000000
--- a/ui/src/components/route/PublicRoute.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Navigate, Outlet } from 'react-router-dom'
-import appConfig from '@/proxy/configs/app.config'
-import useAuth from '@/utils/hooks/useAuth'
-
-const { authenticatedEntryPath } = appConfig
-
-const PublicRoute = () => {
- const { authenticated } = useAuth()
-
- return authenticated ? :
-}
-
-export default PublicRoute
diff --git a/ui/src/components/ui/Dialog/DialogExample.tsx b/ui/src/components/ui/Dialog/DialogExample.tsx
deleted file mode 100644
index 8a106bed..00000000
--- a/ui/src/components/ui/Dialog/DialogExample.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import React, { useState } from 'react'
-import Dialog from './Dialog'
-
-const DialogExample: React.FC = () => {
- const [isOpen, setIsOpen] = useState(false)
-
- const handleOpen = () => setIsOpen(true)
- const handleClose = () => setIsOpen(false)
-
- const handleMaximize = () => {
- console.log('Dialog maximized - should cover full screen')
- }
-
- const handleRestore = () => {
- console.log('Dialog restored - should return to normal size')
- }
-
- return (
-
-
- Open Dialog with Window Controls
-
-
-
-
-
Dialog with Window Controls
-
-
- This dialog has maximize and restore buttons in the top-right corner.
-
-
- Click the maximize button (□) to make the dialog fullscreen
- Click the restore button (⧉) to return to normal size
- Click the close button (×) to close the dialog
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
- tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
- veniam, quis nostrud exercitation ullamco laboris.
-
-
- Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
- dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
- proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-
- Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
- doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
- veritatis et quasi architecto beatae vitae dicta sunt explicabo.
-
-
-
-
-
-
- )
-}
-
-export default DialogExample
diff --git a/ui/src/components/visualDesigner/VisualCanvas.tsx b/ui/src/components/visualDesigner/VisualCanvas.tsx
index 366dbc09..f3eec294 100644
--- a/ui/src/components/visualDesigner/VisualCanvas.tsx
+++ b/ui/src/components/visualDesigner/VisualCanvas.tsx
@@ -37,6 +37,7 @@ import {
isFormNode,
normalizeDesignerKeyList,
readFormField,
+ DESIGNER_DROPDOWN_PLACEHOLDER_KEY,
resolveDesignerDropdownTitle,
resolveDesignerPreviewFilterValue,
resolveDesignerTabValue,
@@ -1334,6 +1335,7 @@ const renderElement = (
options,
dropdownProps.activeKey,
dropdownProps.title,
+ translate(DESIGNER_DROPDOWN_PLACEHOLDER_KEY),
)
return (
)}>
diff --git a/ui/src/components/visualDesigner/codeGenerator.ts b/ui/src/components/visualDesigner/codeGenerator.ts
index 344a449e..58038fb5 100644
--- a/ui/src/components/visualDesigner/codeGenerator.ts
+++ b/ui/src/components/visualDesigner/codeGenerator.ts
@@ -30,7 +30,7 @@ import {
DESIGNER_FILTER_DX_OPERATORS,
toDesignerFilterParamName,
normalizeDesignerKeyList,
- DESIGNER_DROPDOWN_PLACEHOLDER,
+ DESIGNER_DROPDOWN_PLACEHOLDER_KEY,
FORM_SLOTS,
SQL_DEFAULT_VALUE_PROP,
type DesignerDataSource,
@@ -1656,6 +1656,29 @@ export const generateDesignerCode = (name: string, document: DesignerDocument) =
const hasDataSourceFilters = dataSources.some(
(source) => getDesignerDataSourceFilters(source).length > 0,
)
+ // Two designer sources can address the very same request — a Form Select and
+ // the Grid bound to the same list is the usual pair. They are grouped by
+ // request identity so the page issues one call instead of one per source; the
+ // duplicates read the canonical state through an alias, which keeps every
+ // `data_` binding in the document working untouched.
+ const firstSourceIdByRequest = new Map()
+ const canonicalSourceId = new Map()
+ dataSources.forEach((source) => {
+ // Filters belong to the identity: the same URL with different filters is a
+ // different request and must keep a state of its own.
+ const requestKey = [
+ source.method,
+ source.url.trim(),
+ source.responsePath || '',
+ filterEntriesExpression(source, sqlRecordByRef),
+ ].join('|')
+ if (!firstSourceIdByRequest.has(requestKey)) firstSourceIdByRequest.set(requestKey, source.id)
+ canonicalSourceId.set(source.id, firstSourceIdByRequest.get(requestKey) as string)
+ })
+ /** Canonical id of the group a Form loads itself; a mount fetch would duplicate it. */
+ const formManagedCanonicalIds = new Set(
+ [...sqlManagedSelectSourceIds].map((id) => canonicalSourceId.get(id) ?? id),
+ )
const hasFilters = hasDataSourceFilters || platformFilterNodes.length > 0
const selectHelpers = hasSelect
? ` const toSelectOptions = (value, labelPath = "", valuePath = "") => {
@@ -1686,7 +1709,7 @@ export const generateDesignerCode = (name: string, document: DesignerDocument) =
const key = activeKey === undefined || activeKey === null ? "" : String(activeKey)
const selected = key ? options.find((option) => String(option?.value ?? "") === key) : undefined
if (selected) return String(selected.label ?? selected.value ?? key)
- return String(fallbackTitle ?? "") || ${JSON.stringify(DESIGNER_DROPDOWN_PLACEHOLDER)}
+ return String(fallbackTitle ?? "") || ${translateExpression(DESIGNER_DROPDOWN_PLACEHOLDER_KEY)}
}`
: ''
// Falls back to the first tab while an endpoint driven tab list is still empty
@@ -1715,19 +1738,29 @@ export const generateDesignerCode = (name: string, document: DesignerDocument) =
}`
: ''
// Grid cells print raw endpoint columns, so dates and decimals are localised
- // here — the culture is published on `` because a runtime compiled
- // component has neither hooks nor imports to reach the store.
+ // here. The culture is injected by the component runtime; `` is the
+ // fallback for an older host, and an empty result hands the browser default to
+ // Intl rather than pinning the page to a language chosen in this file.
const localeHelpers = hasDataTable
- ? ` const localeCulture = () => (typeof document !== "undefined" && document.documentElement.lang) || "en"
- const localeDateOptions = { year: "numeric", month: "2-digit", day: "2-digit" }
- const localeTimeOptions = { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }
+ ? ` const localeCulture = () => {
+ const injected = typeof getCulture === "function" ? getCulture() : ""
+ if (injected) return injected
+ if (typeof document !== "undefined" && document.documentElement.lang) return document.documentElement.lang
+ return undefined
+ }
+ // Patterns come from the culture itself; a fixed day/month/year order would
+ // print the same layout in every language.
+ const localeDateOptions = { dateStyle: "short" }
+ const localeTimeOptions = { dateStyle: "short", timeStyle: "short" }
const localeIsoPattern = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/
const formatLocaleValue = (value) => {
if (value === null || value === undefined) return ""
const culture = localeCulture()
if (typeof value === "number") {
if (!Number.isFinite(value)) return String(value)
- return new Intl.NumberFormat(culture, { useGrouping: false, maximumFractionDigits: 20 }).format(value)
+ // Grouping and the decimal separator are the culture's decision; only the
+ // precision guard stays, so a long decimal is not rounded away.
+ return new Intl.NumberFormat(culture, { maximumFractionDigits: 20 }).format(value)
}
if (value instanceof Date) {
return Number.isNaN(value.getTime()) ? "" : new Intl.DateTimeFormat(culture, localeTimeOptions).format(value)
@@ -1992,7 +2025,18 @@ export const generateDesignerCode = (name: string, document: DesignerDocument) =
const identifier = safeIdentifier(source.id)
// POST/PUT/DELETE sources are only ever invoked by a Form command;
// they hold no readable state and must never be written to.
- if (source.method !== 'GET' || !source.url.trim().startsWith('/api/')) {
+ const isReadable = source.method === 'GET' && source.url.trim().startsWith('/api/')
+ const canonicalId = canonicalSourceId.get(source.id) ?? source.id
+ // A duplicate declares no state of its own: it aliases the canonical
+ // source, so both bindings always read the one response that was fetched.
+ if (canonicalId !== source.id) {
+ const canonical = safeIdentifier(canonicalId)
+ return isReadable
+ ? ` const data_${identifier} = data_${canonical}
+ const setData_${identifier} = setData_${canonical}`
+ : ` const data_${identifier} = data_${canonical}`
+ }
+ if (!isReadable) {
return ` const [data_${identifier}] = React.useState(null)`
}
return ` const [data_${identifier}, setData_${identifier}] = React.useState(null)`
@@ -2002,10 +2046,13 @@ export const generateDesignerCode = (name: string, document: DesignerDocument) =
.map((source) => {
const identifier = safeIdentifier(source.id)
if (source.method !== 'GET' || !source.url.trim().startsWith('/api/')) return ''
+ // A duplicate is only an alias of an earlier source; the canonical one
+ // already owns the single fetch for this request.
+ if ((canonicalSourceId.get(source.id) ?? source.id) !== source.id) return ''
// A GetById style URL is not fetched on mount — requesting a literal `{id}`
// is a guaranteed 400 — and neither is a source the owning Form
// loads itself, through the setter declared above.
- if (hasFormUrlParams(source.url) || sqlManagedSelectSourceIds.has(source.id)) {
+ if (hasFormUrlParams(source.url) || formManagedCanonicalIds.has(source.id)) {
return ''
}
const entries = filterEntriesExpression(source, sqlRecordByRef)
diff --git a/ui/src/components/visualDesigner/types.ts b/ui/src/components/visualDesigner/types.ts
index cb334691..db07bfeb 100644
--- a/ui/src/components/visualDesigner/types.ts
+++ b/ui/src/components/visualDesigner/types.ts
@@ -504,7 +504,8 @@ export const resolveDesignerTabValue = (
return options.length ? String(options[0]?.value ?? '') : ''
}
-export const DESIGNER_DROPDOWN_PLACEHOLDER = 'Seçiniz'
+/** Localization key of the toggle label; the text itself never lives in the code. */
+export const DESIGNER_DROPDOWN_PLACEHOLDER_KEY = '::App.Platform.Select'
/**
* Dropdown renders its toggle from `title` only; `activeKey` merely highlights an
@@ -515,12 +516,13 @@ export const resolveDesignerDropdownTitle = (
options: Array>,
activeKey: unknown,
fallbackTitle: unknown,
+ placeholder: string,
) => {
const key = activeKey === undefined || activeKey === null ? '' : String(activeKey)
const selected = key ? options.find((option) => String(option?.value ?? '') === key) : undefined
if (selected) return String(selected.label ?? selected.value ?? key)
const title = fallbackTitle === undefined || fallbackTitle === null ? '' : String(fallbackTitle)
- return title || DESIGNER_DROPDOWN_PLACEHOLDER
+ return title || placeholder
}
export interface DesignerPropertyInfo extends PropertyInfo {
diff --git a/ui/src/constants/chart.constant.ts b/ui/src/constants/chart.constant.ts
deleted file mode 100644
index 512e7ddb..00000000
--- a/ui/src/constants/chart.constant.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { TW_COLORS } from '@/utils/tailwind'
-const C = TW_COLORS as any
-
-export const COLOR_1 = C.indigo[600]
-export const COLOR_2 = C.blue[500]
-export const COLOR_3 = C.emerald[500]
-export const COLOR_4 = C.amber[500]
-export const COLOR_5 = C.red[500]
-export const COLOR_6 = C.purple[500]
-export const COLOR_7 = C.cyan[500]
-
-export const COLOR_1_LIGHT = C.indigo[100]
-export const COLOR_2_LIGHT = C.blue[100]
-export const COLOR_3_LIGHT = C.emerald[100]
-export const COLOR_4_LIGHT = C.amber[100]
-export const COLOR_5_LIGHT = C.red[100]
-export const COLOR_6_LIGHT = C.purple[100]
-export const COLOR_7_LIGHT = C.cyan[100]
-
-export const COLORS = [
- COLOR_1,
- COLOR_2,
- COLOR_3,
- COLOR_4,
- COLOR_5,
- COLOR_6,
- COLOR_7,
-]
-
-export const COLORS_LIGHT = [
- COLOR_1_LIGHT,
- COLOR_2_LIGHT,
- COLOR_3_LIGHT,
- COLOR_4_LIGHT,
- COLOR_5_LIGHT,
- COLOR_6_LIGHT,
- COLOR_7_LIGHT,
-]
diff --git a/ui/src/contexts/ComponentContext.tsx b/ui/src/contexts/ComponentContext.tsx
index f6686662..7d2cb0f2 100644
--- a/ui/src/contexts/ComponentContext.tsx
+++ b/ui/src/contexts/ComponentContext.tsx
@@ -99,6 +99,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const applicationConfig = useStoreState((state) => state.abpConfig?.config)
+ const currentLang = useStoreState((state) => state.locale.currentLang)
const extraProperties = applicationConfig?.extraProperties
const [components, setComponents] = useState([])
@@ -130,6 +131,16 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
[],
)
+ // Number and date formatting inside a runtime component follows the active
+ // culture. It is read through a ref for the same reason as the two above: a
+ // language change must reach the compiled bundle without recompiling it.
+ const culture =
+ useStoreState((state) => state.abpConfig?.config?.localization?.currentCulture?.cultureName) ??
+ currentLang
+ const cultureRef = useRef(culture)
+ cultureRef.current = culture
+ const stableGetCulture = useCallback(() => cultureRef.current, [])
+
const refreshComponents = useCallback(async () => {
if (!applicationConfig) return
@@ -309,6 +320,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
axios,
translate: stableTranslate,
checkPermission: stableCheckPermission,
+ getCulture: stableGetCulture,
})
const { registry, errors } = compileComponentBundle(
@@ -336,7 +348,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
return () => {
cancelled = true
}
- }, [compilationSignature, stableCheckPermission, stableTranslate])
+ }, [compilationSignature, stableCheckPermission, stableTranslate, stableGetCulture])
const renderComponent = useCallback(
(name: string, props: ComponentProps = {}) => {
diff --git a/ui/src/proxy/dashboard/models.ts b/ui/src/proxy/dashboard/models.ts
deleted file mode 100644
index 544ed8fa..00000000
--- a/ui/src/proxy/dashboard/models.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-export interface DashboardWidgetsRequestDto {
- tarih1: string
- tarih2: string
-}
-
-export interface DashboardWidgetsDto {
- id: number
- label: string
- dataValue: number
- dataGrowShrink: number
- valuePrefix: string
- date: Date
-}
-
-export interface WidgetsData {
- label: string
- datavalue: number
- datagrowShrink: number
- valuePrefix: string
- date: Date
-}
diff --git a/ui/src/proxy/dashboard/widgets.ts b/ui/src/proxy/dashboard/widgets.ts
deleted file mode 100644
index cfd7bf47..00000000
--- a/ui/src/proxy/dashboard/widgets.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import apiService from '../../services/api.service'
-import { DashboardWidgetsRequestDto, DashboardWidgetsDto } from './models'
-
-export const getDashboardWidgetList = (input: DashboardWidgetsRequestDto) => {
- return apiService.fetchData({
- method: 'GET',
- url: `/api/app/dashboard/widgets?Tarih1=${input.tarih1}&Tarih2=${input.tarih2}`,
- })
-}
diff --git a/ui/src/proxy/reports/models.ts b/ui/src/proxy/reports/models.ts
deleted file mode 100644
index 409d147d..00000000
--- a/ui/src/proxy/reports/models.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-export type ReportParameterType = 'text' | 'number' | 'date' | 'select' | 'checkbox'
-
-export interface ReportCategoryDto {
- id: string
- name: string
- description?: string
- icon?: string
-}
-
-export interface ReportParameterDto {
- id: string
- templateId: string
- name: string
- placeholder?: string
- type: ReportParameterType
- defaultValue?: string
- required: boolean
- description?: string
-}
-
-export interface ReportTemplateDto {
- id: string
- name: string
- description?: string
- htmlContent: string
- categoryId?: string
- tags: string[]
- parameters: ReportParameterDto[]
-
- // FullAuditedEntityDto alanları
- creationTime: string // ISO
- lastModificationTime?: string // ISO | undefined
- creatorId?: string
- lastModifierId?: string
-}
-
-export interface ReportGeneratedDto {
- id: string
- templateId?: string | null
- templateName: string
- generatedContent: string
- parameters: Record
-
- // FullAuditedEntityDto alanları
- creationTime: string // ISO
- lastModificationTime?: string // ISO | undefined
- creatorId?: string
- lastModifierId?: string
-
- template?: ReportTemplateDto // dolu gelebilir
-}
-
-/** Create / Update input’ları */
-export interface CreateReportParameterDto {
- name: string
- placeholder?: string
- type: ReportParameterType
- defaultValue?: string
- required: boolean
- description?: string
-}
-
-export interface UpdateReportParameterDto extends CreateReportParameterDto {
- id?: string // opsiyonel
-}
-
-export interface CreateReportTemplateDto {
- name: string
- description?: string
- htmlContent: string
- categoryId?: string
- tags?: string[]
- parameters: CreateReportParameterDto[]
-}
-
-export interface UpdateReportTemplateDto {
- name: string
- description?: string
- htmlContent: string
- categoryId?: string
- tags?: string[]
- parameters: UpdateReportParameterDto[]
-}
-
-/** Generate input’u */
-export interface ReportGenerateDto {
- templateId: string
- parameters: Record
-}
-
-/** List input’ları (query string) */
-export interface GetReportTemplatesInput {
- skipCount?: number
- maxResultCount?: number
- sorting?: string
- filter?: string
- categoryId?: string
-}
-
-export interface GetReportsGeneratedInput {
- skipCount?: number
- maxResultCount?: number
- sorting?: string
- filter?: string
- templateId?: string
-}
diff --git a/ui/src/routes/dynamicRouteLoader.tsx b/ui/src/routes/dynamicRouteLoader.tsx
index 2dfa66fe..1f69911e 100644
--- a/ui/src/routes/dynamicRouteLoader.tsx
+++ b/ui/src/routes/dynamicRouteLoader.tsx
@@ -13,6 +13,18 @@ const physicalComponentCache = new Map<
React.LazyExoticComponent>
>()
+// Runtime component wrappers are cached by name so their identity survives every
+// re-render of the router. Without this, each render produced a brand new
+// function type and React remounted the whole page — every data hook inside the
+// component fired again (twice on a language change, once per configuration
+// refresh).
+const runtimeComponentCache = new Map>()
+// The wrapper must not close over `renderComponent`: it changes after the
+// asynchronous bundle compilation finishes, and an old closure would render an
+// empty page. The latest renderer is kept beside the wrapper and read at render
+// time instead.
+const runtimeRenderers = new Map React.ReactNode>()
+
// Fiziksel komponent yükleme (mevcut mantık)
function loadPhysicalComponent(componentPath: string) {
const cleanedPath = componentPath.replace(/^@\//, '')
@@ -50,19 +62,21 @@ function loadDynamicComponent(
renderComponent &&
isComponentRegistered(componentPath)
) {
- DynamicComponent = (props: any) => renderComponent(componentPath, props) as React.ReactElement
+ runtimeRenderers.set(componentPath, renderComponent)
+ let cached = runtimeComponentCache.get(componentPath)
+ if (!cached) {
+ cached = (props: any) =>
+ runtimeRenderers.get(componentPath)?.(componentPath, props) as React.ReactElement
+ cached.displayName = `RuntimeComponent(${componentPath})`
+ runtimeComponentCache.set(componentPath, cached)
+ }
+ DynamicComponent = cached
}
if (!DynamicComponent) {
- if (isComponentRegistered) {
- console.log('Database component registry available - checking...')
- }
throw new Error(`Dynamic component not found: ${componentPath}`)
}
- // Do not put this wrapper in a module-level cache. renderComponent changes
- // after the asynchronous database bundle compilation finishes. Caching this
- // function would keep the pre-compilation closure and render an empty page.
return DynamicComponent as React.ComponentType
}
diff --git a/ui/src/services/question.service.ts b/ui/src/services/question.service.ts
deleted file mode 100644
index cfbf4c6a..00000000
--- a/ui/src/services/question.service.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { PagedResultDto } from '@/proxy'
-import apiService from './api.service'
-import { QuestionDto } from '@/types/coordinator'
-
-class QuestionService {
- async getQuestions(): Promise> {
- const response = await apiService.fetchData>({
- url: '/api/app/question',
- method: 'GET',
- })
- return response.data
- }
-
- async getQuestion(id: string): Promise {
- const response = await apiService.fetchData({
- url: `/api/app/question/${id}`,
- method: 'GET',
- })
- return response.data
- }
-
- async updateQuestion(id: string, input: QuestionDto) {
- const response = await apiService.fetchData({
- url: `/api/app/question/${id}`,
- method: 'PUT',
- data: input as any,
- })
- return response.data
- }
-
- async createQuestion(input: QuestionDto) {
- const response = await apiService.fetchData({
- method: 'POST',
- url: '/api/app/question',
- data: input as any,
- })
- return response.data
- }
-
- async deleteQuestion(id: string) {
- await apiService.fetchData({
- method: 'DELETE',
- url: `/api/app/question/${id}`,
- })
- }
-}
-
-export const questionService = new QuestionService()
diff --git a/ui/src/services/reports.service.ts b/ui/src/services/reports.service.ts
deleted file mode 100644
index 298e7314..00000000
--- a/ui/src/services/reports.service.ts
+++ /dev/null
@@ -1,139 +0,0 @@
-import {
- ReportTemplateDto,
- ReportGeneratedDto,
- CreateReportTemplateDto,
- UpdateReportTemplateDto,
- ReportGenerateDto,
- ReportCategoryDto,
- GetReportTemplatesInput, // backend'deki GenerateReportDto (templateId + parameters)
-} from '@/proxy/reports/models'
-import apiService from './api.service'
-import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
-
-export interface ReportsData {
- templates: ReportTemplateDto[]
- generatedReports: ReportGeneratedDto[]
-}
-
-export class ReportsService {
- apiName = 'Default'
-
- getCategories = () =>
- apiService.fetchData(
- {
- method: 'GET',
- url: '/api/app/report/categories',
- },
- { apiName: this.apiName },
- )
-
- // TEMPLATES
- getTemplates = (input: GetReportTemplatesInput) =>
- apiService.fetchData, PagedAndSortedResultRequestDto>(
- {
- method: 'GET',
- url: '/api/app/report/templates', // ✔ Swagger: GET /api/app/report/templates
- params: {
- sorting: input.sorting,
- skipCount: input.skipCount,
- maxResultCount: input.maxResultCount,
- filter: input.filter,
- categoryId: input.categoryId,
- },
- },
- { apiName: this.apiName },
- )
-
- getTemplateById = (id: string) =>
- apiService.fetchData(
- {
- method: 'GET',
- url: `/api/app/report/${id}/template`, // ✔ Swagger: GET /api/app/report/{id}/template
- },
- { apiName: this.apiName },
- )
-
- createTemplate = (input: CreateReportTemplateDto) =>
- apiService.fetchData(
- {
- method: 'POST',
- url: '/api/app/report/template', // ✔ Swagger: POST /api/app/report/template
- data: input,
- },
- { apiName: this.apiName },
- )
-
- updateTemplate = (id: string, input: UpdateReportTemplateDto) =>
- apiService.fetchData(
- {
- method: 'PUT',
- url: `/api/app/report/${id}/template`, // ✔ Swagger: PUT /api/app/report/{id}/template
- data: input,
- },
- { apiName: this.apiName },
- )
-
- deleteTemplate = (id: string) =>
- apiService.fetchData(
- {
- method: 'DELETE',
- url: `/api/app/report/${id}/template`, // ✔ Swagger: DELETE /api/app/report/{id}/template
- },
- { apiName: this.apiName },
- )
-
- // GENERATED REPORTS
- getGeneratedReports = (input: PagedAndSortedResultRequestDto) =>
- apiService.fetchData, PagedAndSortedResultRequestDto>(
- {
- method: 'GET',
- url: '/api/app/report/generated-reports', // ✔ Swagger: GET /api/app/report/generated-reports
- params: {
- sorting: input.sorting,
- skipCount: input.skipCount,
- maxResultCount: input.maxResultCount,
- },
- },
- { apiName: this.apiName },
- )
-
- getGeneratedReportById = (id: string) =>
- apiService.fetchData(
- {
- method: 'GET',
- url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: GET /api/app/report/{id}/generated-report
- },
- { apiName: this.apiName },
- )
-
- generateReport = (input: ReportGenerateDto) =>
- apiService.fetchData(
- {
- method: 'POST',
- url: '/api/app/report/generate-report', // ✔ Swagger: POST /api/app/report/generate-report
- data: input,
- },
- { apiName: this.apiName },
- )
-
- deleteGeneratedReport = (id: string) =>
- apiService.fetchData(
- {
- method: 'DELETE',
- url: `/api/app/report/${id}/generated-report`, // ✔ Swagger: DELETE /api/app/report/{id}/generated-report
- },
- { apiName: this.apiName },
- )
-
- // BULK
- getAllData = () =>
- apiService.fetchData(
- {
- method: 'GET',
- url: '/api/app/report/data', // ✔ Swagger: GET /api/app/report/data
- },
- { apiName: this.apiName },
- )
-}
-
-export default ReportsService
diff --git a/ui/src/services/route.service.ts b/ui/src/services/route.service.ts
deleted file mode 100644
index 2a842046..00000000
--- a/ui/src/services/route.service.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { RouteDto } from '@/proxy/routes/models'
-import apiService, { Config } from '@/services/api.service'
-
-export class RouteService {
- apiName = 'Default'
-
- getRoutes = (config?: Partial) =>
- apiService.fetchData(
- {
- method: 'GET',
- url: '/api/app/route',
- },
- { apiName: this.apiName, ...config },
- )
-}
-
-const routeService = new RouteService()
-export default routeService
diff --git a/ui/src/types/common.ts b/ui/src/types/common.ts
deleted file mode 100644
index defc004c..00000000
--- a/ui/src/types/common.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export interface WidgetGroupDto {
- // Widget Grubu
- colGap?: number
- colSpan?: number
- className?: string
- items: WidgetEditDto[]
-}
-
-export interface WidgetEditDto {
- // Widget Düzenleme
- colGap: number
- colSpan: number
- sqlQuery?: string
- className?: string
- title: string
- value: string
- valueClassName: string
- color: string
- icon: string
- subTitle: string
- onClick: string
-}
\ No newline at end of file
diff --git a/ui/src/types/coordinator.ts b/ui/src/types/coordinator.ts
deleted file mode 100644
index c851461e..00000000
--- a/ui/src/types/coordinator.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import { FullAuditedEntityDto } from "@/proxy";
-
-export type QuestionType =
- | 'multiple-choice'
- | 'fill-blank'
- | 'multiple-answer'
- | 'matching'
- | 'ordering'
- | 'open-ended'
- | 'true-false'
- | 'calculation'
-
-export const QUESTION_TYPE_LABELS: Record = {
- 'multiple-choice': 'Multiple Choice',
- 'fill-blank': 'Fill in the Blank',
- 'multiple-answer': 'Multiple Answer',
- 'matching': 'Matching',
- 'ordering': 'Ordering',
- 'open-ended': 'Open Ended',
- 'true-false': 'True / False',
- 'calculation': 'Calculation',
-}
-
-export type ExamType = "exam" | "assignment" | "test";
-export type TestType = 'pdf' | 'image';
-export type MediaType = 'image' | 'video';
-export type QuestionDifficulty = 'easy' | 'medium' | 'hard';
-export type ExamSessionStatus = 'in-progress' | 'completed' | 'submitted';
-
-export interface QuestionPoolDto extends FullAuditedEntityDto {
- name: string;
- description: string;
- questions: QuestionDto[];
- tags: string[];
-}
-
-export interface QuestionDto extends FullAuditedEntityDto {
- questionType: QuestionType;
- title: string;
- content: string;
- mediaUrl?: string;
- mediaType?: MediaType;
- options?: QuestionOptionDto[];
- correctAnswer?: string | string[];
- points: number;
- timeLimit?: number;
- explanation?: string;
- difficulty: QuestionDifficulty;
-}
-
-export interface QuestionOptionDto extends FullAuditedEntityDto {
- text: string;
- isCorrect: boolean;
-}
-
-export interface Exam {
- id: string;
- title: string;
- description: string;
- type: ExamType;
- testDocument?: {
- url: string;
- type: TestType;
- name: string;
- };
- answerKeyTemplate?: AnswerKeyItem[];
- questions: QuestionDto[];
- timeLimit: number;
- totalPoints: number;
- passingScore: number;
- allowReview: boolean;
- randomizeQuestions: boolean;
- showResults: boolean;
- maxAttempts: number;
- startTime?: Date;
- endTime?: Date;
- isActive: boolean;
- creationTime: Date;
- lastModificationTime: Date;
-}
-
-export interface AnswerKeyItem {
- id: string;
- questionNumber: number;
- type: QuestionType;
- options?: string[];
- points: number;
- correctAnswer?: string | string[];
-}
-
-export interface StudentAnswer {
- questionId: string;
- answer: string | string[];
- timeSpent: number;
- isCorrect?: boolean;
- points?: number;
-}
-
-export interface ExamSession {
- id: string;
- examId: string;
- studentId: string;
- startTime: Date;
- endTime?: Date;
- answers: StudentAnswer[];
- totalScore?: number;
- status: ExamSessionStatus;
- timeRemaining: number;
-}
-
-export interface TagItem {
- id: string;
- name: string;
- description: string;
- color: string;
- usageCount: number;
- creationTime: Date;
-}
-
-export interface NavigationItem {
- id: string;
- title: string;
- icon: string;
- path: string;
- description?: string;
- badge?: number;
-}
-
-export interface AdminRoute {
- path: string;
- component: string;
- title: string;
- breadcrumb: string[];
-}
\ No newline at end of file
diff --git a/ui/src/types/intranet.ts b/ui/src/types/intranet.ts
deleted file mode 100644
index c042cd51..00000000
--- a/ui/src/types/intranet.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-export enum LeaveStatusEnum {
- // İzin Durumu
- Pending = 'Pending', // Beklemede
- Approved = 'Approved', // Onaylandı
- Rejected = 'Rejected', // Reddedildi
- Cancelled = 'Cancelled', // İptal edildi
-}
-
-export enum LeaveTypeEnum {
- // İzin Türü
- Annual = 'Annual', // Yıllık
- Sick = 'Sick', // Hastalık
- Maternity = 'Maternity', // Doğum
- Paternity = 'Paternity', // Babalık
- Personal = 'Personal', // Kişisel
- Emergency = 'Emergency', // Acil
- Study = 'Study', // Eğitim
- Unpaid = 'Unpaid', // Ücretsiz
-}
-
-export enum PriorityEnum {
- // Öncelik
- Low = 'LOW', // Düşük
- Normal = 'NORMAL', // Normal
- High = 'HIGH', // Yüksek
- Urgent = 'URGENT', // Acil
-}
-
-export enum TaskStatusEnum {
- // Görev Durumu
- NotStarted = 'NOT_STARTED', // Başlanmadı
- InProgress = 'IN_PROGRESS', // Devam Ediyor
- Completed = 'COMPLETED', // Tamamlandı
- OnHold = 'ON_HOLD', // Beklemede
- Cancelled = 'CANCELLED', // İptal Edildi
-}
\ No newline at end of file
diff --git a/ui/src/utils/deepParseJson.ts b/ui/src/utils/deepParseJson.ts
deleted file mode 100644
index 14ea4d80..00000000
--- a/ui/src/utils/deepParseJson.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-const isNumString = (str: string): boolean => !isNaN(Number(str))
-
-type JsonObject = { [key: string]: unknown }
-type JsonArray = Array
-type Json = JsonObject | JsonArray | string | number | boolean | null
-
-function deepParseJson(jsonString: Json): Json {
- if (typeof jsonString === 'string') {
- if (isNumString(jsonString)) {
- return jsonString
- }
- try {
- return deepParseJson(JSON.parse(jsonString))
- } catch {
- return jsonString
- }
- } else if (Array.isArray(jsonString)) {
- return jsonString.map((val) => deepParseJson(val as JsonArray))
- } else if (typeof jsonString === 'object' && jsonString !== null) {
- return Object.keys(jsonString).reduce((obj, key) => {
- const val = jsonString[key]
- obj[key] = isNumString(val as string)
- ? val
- : deepParseJson(val as number)
- return obj
- }, {})
- } else {
- return jsonString
- }
-}
-
-export default deepParseJson
diff --git a/ui/src/utils/hooks/useClassroomLogic.ts b/ui/src/utils/hooks/useClassroomLogic.ts
deleted file mode 100644
index 888810e4..00000000
--- a/ui/src/utils/hooks/useClassroomLogic.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Role, RoleState, VideoroomDto } from '@/proxy/videoroom/models'
-import { useStoreActions, useStoreState } from '@/store/store'
-import { useState } from 'react'
-
-export function useVideoroomLogic() {
- const { user } = useStoreState((state) => state.auth)
- const { setUser } = useStoreActions((actions) => actions.auth.user)
-
- const [roleState, setRoleState] = useState('role-selection')
- const [currentClass, setCurrentClass] = useState(null)
- const [allClasses, setAllClasses] = useState([])
-
- const handleRoleSelect = (role: Role) => {
- setUser({
- ...user,
- role,
- })
- setRoleState('dashboard')
- }
-
- const handleCreateClass = (classData: Partial) => {
- const newClass = {
- ...classData,
- id: crypto.randomUUID(),
- teacherId: '',
- teacherName: '',
- isActive: false,
- isScheduled: true,
- participantCount: 0,
- } as VideoroomDto
- setAllClasses((prev) => [...prev, newClass])
- }
-
- const handleEditClass = (classId: string, classData: Partial) => {
- setAllClasses((prev) => prev.map((c) => (c.id === classId ? { ...c, ...classData } : c)))
- }
-
- const handleDeleteClass = (classId: string) => {
- setAllClasses((prev) => prev.filter((c) => c.id !== classId))
- }
-
- return {
- roleState,
- setRoleState,
- currentClass,
- setCurrentClass,
- allClasses,
- setAllClasses,
- handleRoleSelect,
- handleCreateClass,
- handleEditClass,
- handleDeleteClass,
- }
-}
diff --git a/ui/src/utils/hooks/useCoordinator.ts b/ui/src/utils/hooks/useCoordinator.ts
deleted file mode 100644
index 6e818237..00000000
--- a/ui/src/utils/hooks/useCoordinator.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import { QuestionPoolDto, Exam, TagItem, ExamSession } from "@/types/coordinator";
-import { useState } from "react";
-
-export function useCoordinator() {
- const [currentPath, setCurrentPath] = useState("/admin/dashboard");
- const [pools, setPools] = useState([]);
- const [exams, setExams] = useState([]);
- const [tags, setTags] = useState([]);
- const [currentExam, setCurrentExam] = useState(null);
-
- const handleUpdateTest = (updatedTest: Exam) => {
- setExams((prev) =>
- prev.map((exam) => (exam.id === updatedTest.id ? updatedTest : exam))
- );
- };
-
- const handleDeleteTest = (testId: string) => {
- setExams((prev) => prev.filter((exam) => exam.id !== testId));
- };
-
- const handleCreatePool = (
- poolData: Omit
- ) => {
- const newPool: QuestionPoolDto = {
- ...poolData,
- id: `pool-${Date.now()}`,
- creationTime: new Date(),
- };
- setPools((prev) => [...prev, newPool]);
- };
-
- const handleUpdatePool = (updatedPool: QuestionPoolDto) => {
- setPools((prev) =>
- prev.map((pool) => (pool.id === updatedPool.id ? updatedPool : pool))
- );
- };
-
- const handleDeletePool = (poolId: string) => {
- setPools((prev) => prev.filter((pool) => pool.id !== poolId));
- };
-
- const handleCreateExam = (
- examData: Omit
- ) => {
- const newExam: Exam = {
- ...examData,
- id: `exam-${Date.now()}`,
- creationTime: new Date(),
- lastModificationTime: new Date(),
- };
- setExams((prev) => [...prev, newExam]);
- setCurrentPath("/admin/exams");
- };
-
- const handleSaveExam = (exam: Exam) => {
- setExams((prev) => prev.map((e) => (e.id === exam.id ? exam : e)));
- setCurrentPath("/admin/exams");
- };
-
- const handleCreateTest = (
- testData: Omit
- ) => {
- const newTest: Exam = {
- ...testData,
- id: `test-${Date.now()}`,
- creationTime: new Date(),
- lastModificationTime: new Date(),
- };
- setExams((prev) => [...prev, newTest]);
- setCurrentPath("/admin/tests");
- };
-
- const handleCreateTag = (
- tagData: Omit
- ) => {
- const newTag: TagItem = {
- ...tagData,
- id: `tag-${Date.now()}`,
- usageCount: 0,
- creationTime: new Date(),
- };
- setTags((prev) => [...prev, newTag]);
- };
-
- const handleUpdateTag = (updatedTag: TagItem) => {
- setTags((prev) =>
- prev.map((tag) => (tag.id === updatedTag.id ? updatedTag : tag))
- );
- };
-
- const handleDeleteTag = (tagId: string) => {
- setTags((prev) => prev.filter((tag) => tag.id !== tagId));
- };
-
- const handleExamComplete = (session: ExamSession) => {
- alert("Assessment completed successfully!");
- setCurrentPath("/admin/dashboard");
- setCurrentExam(null);
- };
-
- const startExam = (exam: Exam) => {
- setCurrentExam(exam);
- };
-
- return {
- currentPath,
- setCurrentPath,
- pools,
- setPools,
- exams,
- setExams,
- tags,
- setTags,
- currentExam,
- setCurrentExam,
- handleUpdateTest,
- handleDeleteTest,
- handleCreatePool,
- handleUpdatePool,
- handleDeletePool,
- handleCreateExam,
- handleSaveExam,
- handleCreateTest,
- handleCreateTag,
- handleUpdateTag,
- handleDeleteTag,
- handleExamComplete,
- startExam,
- };
-}
diff --git a/ui/src/utils/hooks/useExamSecurity.ts b/ui/src/utils/hooks/useExamSecurity.ts
deleted file mode 100644
index 01e63155..00000000
--- a/ui/src/utils/hooks/useExamSecurity.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { useEffect, useCallback } from 'react';
-
-interface SecurityConfig {
- disableRightClick: boolean;
- disableCopyPaste: boolean;
- disableDevTools: boolean;
- fullScreenMode: boolean;
- preventTabSwitch: boolean;
-}
-
-export const useExamSecurity = (config: SecurityConfig, isActive: boolean = true) => {
- const handleRightClick = useCallback((e: MouseEvent) => {
- if (config.disableRightClick && isActive) {
- e.preventDefault();
- return false;
- }
- }, [config.disableRightClick, isActive]);
-
- const handleKeyDown = useCallback((e: KeyboardEvent) => {
- if (!isActive) return;
-
- // Disable copy/paste shortcuts
- if (config.disableCopyPaste) {
- if (e.ctrlKey && (e.key === 'c' || e.key === 'v' || e.key === 'x' || e.key === 'a')) {
- e.preventDefault();
- return false;
- }
- }
-
- // Disable developer tools
- if (config.disableDevTools) {
- if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && e.key === 'I')) {
- e.preventDefault();
- return false;
- }
- }
-
- // Disable Alt+Tab for tab switching
- if (config.preventTabSwitch && e.altKey && e.key === 'Tab') {
- e.preventDefault();
- return false;
- }
- }, [config, isActive]);
-
- const handleVisibilityChange = useCallback(() => {
- if (config.preventTabSwitch && isActive && document.hidden) {
- // Log tab switch attempt - in real app, this would call an API
- console.warn('Tab switch detected during exam');
- }
- }, [config.preventTabSwitch, isActive]);
-
- const enterFullScreen = useCallback(async () => {
- if (config.fullScreenMode && isActive) {
- try {
- await document.documentElement.requestFullscreen();
- } catch (error) {
- console.error('Failed to enter fullscreen:', error);
- }
- }
- }, [config.fullScreenMode, isActive]);
-
- const exitFullScreen = useCallback(async () => {
- if (document.fullscreenElement) {
- try {
- await document.exitFullscreen();
- } catch (error) {
- console.error('Failed to exit fullscreen:', error);
- }
- }
- }, []);
-
- useEffect(() => {
- if (isActive) {
- document.addEventListener('contextmenu', handleRightClick);
- document.addEventListener('keydown', handleKeyDown);
- document.addEventListener('visibilitychange', handleVisibilityChange);
-
- return () => {
- document.removeEventListener('contextmenu', handleRightClick);
- document.removeEventListener('keydown', handleKeyDown);
- document.removeEventListener('visibilitychange', handleVisibilityChange);
-
- if (config.fullScreenMode) {
- exitFullScreen();
- }
- };
- }
- }, [handleRightClick, handleKeyDown, handleVisibilityChange, exitFullScreen, isActive, config.fullScreenMode]);
-
- return {
- enterFullScreen,
- exitFullScreen,
- isFullScreen: !!document.fullscreenElement
- };
-};
\ No newline at end of file
diff --git a/ui/src/utils/hooks/useExamTimer.ts b/ui/src/utils/hooks/useExamTimer.ts
deleted file mode 100644
index 60e6988b..00000000
--- a/ui/src/utils/hooks/useExamTimer.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { useState, useEffect, useCallback } from 'react';
-
-interface UseExamTimerProps {
- initialTime: number; // in seconds
- onTimeUp?: () => void;
- onTick?: (timeRemaining: number) => void;
- autoStart?: boolean;
-}
-
-export const useExamTimer = ({
- initialTime,
- onTimeUp,
- onTick,
- autoStart = false
-}: UseExamTimerProps) => {
- const [timeRemaining, setTimeRemaining] = useState(initialTime);
- const [isRunning, setIsRunning] = useState(autoStart);
- const [isPaused, setIsPaused] = useState(false);
-
- const start = useCallback(() => {
- setIsRunning(true);
- setIsPaused(false);
- }, []);
-
- const pause = useCallback(() => {
- setIsPaused(true);
- }, []);
-
- const resume = useCallback(() => {
- setIsPaused(false);
- }, []);
-
- const reset = useCallback(() => {
- setTimeRemaining(initialTime);
- setIsRunning(false);
- setIsPaused(false);
- }, [initialTime]);
-
- const stop = useCallback(() => {
- setIsRunning(false);
- setIsPaused(false);
- }, []);
-
- useEffect(() => {
- let interval: ReturnType;
-
- if (isRunning && !isPaused && timeRemaining > 0) {
- interval = setInterval(() => {
- setTimeRemaining((prev) => {
- const newTime = prev - 1;
- onTick?.(newTime);
-
- if (newTime <= 0) {
- setIsRunning(false);
- onTimeUp?.();
- return 0;
- }
-
- return newTime;
- });
- }, 1000);
- }
-
- return () => {
- if (interval) {
- clearInterval(interval);
- }
- };
- }, [isRunning, isPaused, timeRemaining, onTimeUp, onTick]);
-
- const formatTime = useCallback((seconds: number) => {
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- const secs = seconds % 60;
-
- if (hours > 0) {
- return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- }
- return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- }, []);
-
- return {
- timeRemaining,
- formattedTime: formatTime(timeRemaining),
- isRunning,
- isPaused,
- start,
- pause,
- resume,
- reset,
- stop,
- progress: ((initialTime - timeRemaining) / initialTime) * 100
- };
-};
\ No newline at end of file
diff --git a/ui/src/utils/hooks/useOrders.ts b/ui/src/utils/hooks/useOrders.ts
deleted file mode 100644
index b2b62bb5..00000000
--- a/ui/src/utils/hooks/useOrders.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Product } from '@/proxy/order/models'
-import { useState, useEffect } from 'react'
-
-export const useOrders = () => {
- const [products, setProducts] = useState([])
- const [loading, setLoading] = useState(true)
- const [error, setError] = useState(null)
-
- useEffect(() => {
- fetchProducts()
- }, [])
-
- const fetchProducts = async () => {
- try {
- setLoading(true)
- setProducts(products)
- setError(null)
- } catch (err) {
- console.error('Products fetch error:', err)
- setError('App.Orders.LoadProductsError')
- } finally {
- setLoading(false)
- }
- }
-
- return { products, loading, error, refetch: fetchProducts }
-}
diff --git a/ui/src/utils/hooks/useReports.ts b/ui/src/utils/hooks/useReports.ts
deleted file mode 100644
index 2fefe122..00000000
--- a/ui/src/utils/hooks/useReports.ts
+++ /dev/null
@@ -1,227 +0,0 @@
-import { ReportGeneratedDto, ReportTemplateDto, ReportCategoryDto } from '@/proxy/reports/models'
-import ReportsService from '@/services/reports.service'
-import { useState, useCallback, useEffect } from 'react'
-
-const reportsService = new ReportsService()
-
-interface ReportData {
- templates: ReportTemplateDto[]
- generatedReports: ReportGeneratedDto[]
- categories: ReportCategoryDto[]
-}
-
-export const useReports = () => {
- const [data, setData] = useState({
- templates: [],
- generatedReports: [],
- categories: [],
- })
- const [isLoading, setIsLoading] = useState(false)
-
- useEffect(() => {
- const loadData = async () => {
- setIsLoading(true)
- try {
- await new Promise((resolve) => setTimeout(resolve, 200))
-
- const [templatesResponse, generatedReportsResponse, categoriesResponse] = await Promise.all(
- [
- reportsService.getTemplates({
- sorting: '',
- skipCount: 0,
- maxResultCount: 1000,
- }),
- reportsService.getGeneratedReports({
- sorting: '',
- skipCount: 0,
- maxResultCount: 1000,
- }),
- reportsService.getCategories(),
- ],
- )
-
- setData({
- templates: templatesResponse.data.items || [],
- generatedReports: generatedReportsResponse.data.items || [],
- categories: categoriesResponse.data || [],
- })
- } catch (error) {
- console.error('Error loading data:', error)
- // Fallback to default data
- setData({
- templates: [],
- generatedReports: [],
- categories: [],
- })
- } finally {
- setIsLoading(false)
- }
- }
-
- loadData()
- }, [])
-
- const createTemplate = useCallback(
- async (template: ReportTemplateDto) => {
- setIsLoading(true)
- try {
- const response = await reportsService.createTemplate(template as ReportTemplateDto)
- const newTemplate = response.data as ReportTemplateDto
-
- // Update local state
- setData((prevData) => ({
- ...prevData,
- templates: [...prevData.templates, newTemplate],
- }))
-
- return newTemplate
- } catch (error) {
- console.error('Error creating template:', error)
- throw error
- } finally {
- setIsLoading(false)
- }
- },
- [data],
- )
-
- const updateTemplate = useCallback(async (id: string, updates: Partial) => {
- setIsLoading(true)
- try {
- // First get the current template to merge with updates
- const currentTemplateResponse = await reportsService.getTemplateById(id)
- const currentTemplate = currentTemplateResponse.data as ReportTemplateDto
-
- const updatedTemplate = { ...currentTemplate, ...updates }
- await reportsService.updateTemplate(id, updatedTemplate)
-
- // Update local state
- setData((prevData) => ({
- ...prevData,
- templates: prevData.templates.map((template) =>
- template.id === id
- ? { ...template, ...updates, lastModificationTime: new Date().toISOString() }
- : template,
- ),
- }))
- } catch (error) {
- console.error('Error updating template:', error)
- throw error
- } finally {
- setIsLoading(false)
- }
- }, [])
-
- const deleteTemplate = useCallback(async (id: string) => {
- setIsLoading(true)
- try {
- await reportsService.deleteTemplate(id)
-
- // Update local state
- setData((prevData) => ({
- ...prevData,
- templates: prevData.templates.filter((template) => template.id !== id),
- }))
- } catch (error) {
- console.error('Error deleting template:', error)
- throw error
- } finally {
- setIsLoading(false)
- }
- }, [])
-
- const generateReport = useCallback(
- async (templateId: string, parameterValues: Record) => {
- setIsLoading(true)
- try {
- const reportData = {
- templateId,
- parameters: parameterValues,
- }
-
- const response = await reportsService.generateReport(reportData)
- const report = response.data as ReportGeneratedDto
-
- if (report) {
- // Update local state
- setData((prevData) => ({
- ...prevData,
- generatedReports: [...prevData.generatedReports, report],
- }))
- }
-
- return report
- } catch (error) {
- console.error('Error generating report:', error)
- throw error
- } finally {
- setIsLoading(false)
- }
- },
- [],
- )
-
- const getReportById = useCallback(
- async (reportId: string) => {
- try {
- const response = await reportsService.getGeneratedReportById(reportId)
- return response.data as ReportGeneratedDto
- } catch (error) {
- console.error('Error getting report by id:', error)
- // Fallback to local data
- return data.generatedReports.find((report) => report.id === reportId)
- }
- },
- [data.generatedReports],
- )
-
- const getTemplateById = useCallback(
- async (templateId: string) => {
- try {
- const response = await reportsService.getTemplateById(templateId)
- return response.data as ReportTemplateDto
- } catch (error) {
- console.error('Error getting template by id:', error)
- // Fallback to local data
- return data.templates.find((template) => template.id === templateId)
- }
- },
- [data.templates],
- )
-
- const loadTemplatesByCategory = useCallback(async (categoryId?: string) => {
- setIsLoading(true)
- try {
- const templatesResponse = await reportsService.getTemplates({
- sorting: '',
- skipCount: 0,
- maxResultCount: 1000,
- categoryId: categoryId && categoryId !== 'Tümü' ? categoryId : undefined,
- })
-
- setData((prevData) => ({
- ...prevData,
- templates: templatesResponse.data.items || [],
- }))
- } catch (error) {
- console.error('Error loading templates by category:', error)
- } finally {
- setIsLoading(false)
- }
- }, [])
-
- return {
- templates: data.templates,
- generatedReports: data.generatedReports,
- categories: data.categories,
- isLoading,
- setIsLoading,
- createTemplate,
- updateTemplate,
- deleteTemplate,
- generateReport,
- getReportById,
- getTemplateById,
- loadTemplatesByCategory,
- }
-}
diff --git a/ui/src/utils/paginate.ts b/ui/src/utils/paginate.ts
deleted file mode 100644
index 91c72ae0..00000000
--- a/ui/src/utils/paginate.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const paginate = (array: Array, pageSize: number, pageNumber: number) => {
- return array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize)
-}
-
-export default paginate
diff --git a/ui/src/utils/requiredFieldValidation.ts b/ui/src/utils/requiredFieldValidation.ts
deleted file mode 100644
index 20d170f4..00000000
--- a/ui/src/utils/requiredFieldValidation.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-export default function requiredFieldValidation(
- value: any,
- message: string
-): string {
- let validationMessage = ''
- if (!value) {
- validationMessage = message || 'Required'
- }
- return validationMessage
-}
diff --git a/ui/src/utils/shadeColor.ts b/ui/src/utils/shadeColor.ts
deleted file mode 100644
index 4026c8c4..00000000
--- a/ui/src/utils/shadeColor.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-const shadeColor = (color: string, percent: number) => {
- let R = parseInt(color.substring(1, 3), 16)
- let G = parseInt(color.substring(3, 5), 16)
- let B = parseInt(color.substring(5, 7), 16)
- R = (R * (100 + percent)) / 100
- G = (G * (100 + percent)) / 100
- B = (B * (100 + percent)) / 100
- R = R < 255 ? R : 255
- G = G < 255 ? G : 255
- B = B < 255 ? B : 255
- const RR =
- R.toString(16).length === 1 ? `0${R.toString(16)}` : R.toString(16)
- const GG =
- G.toString(16).length === 1 ? `0${G.toString(16)}` : G.toString(16)
- const BB =
- B.toString(16).length === 1 ? `0${B.toString(16)}` : B.toString(16)
- return `#${RR}${GG}${BB}`
-}
-
-export default shadeColor
diff --git a/ui/src/utils/sortBy.ts b/ui/src/utils/sortBy.ts
deleted file mode 100644
index cf8d8b79..00000000
--- a/ui/src/utils/sortBy.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-type Primitive = string | number | boolean
-
-export type Primer = (value: Primitive) => Primitive
-
-const sortBy = >(
- field: keyof T,
- reverse: boolean,
- primer?: (value: Primitive) => Primitive
-) => {
- const key = primer
- ? function (x: T) {
- return primer(x[field])
- }
- : function (x: T) {
- return x[field]
- }
- const isReverse = !reverse ? 1 : -1
- return function (a: T, b: T) {
- const valueA = key(a)
- const valueB = key(b)
- if (typeof valueA === 'string' && typeof valueB === 'string') {
- return isReverse * valueA.localeCompare(valueB)
- }
- return isReverse * (valueA > valueB ? 1 : valueB > valueA ? -1 : 0)
- }
-}
-
-export default sortBy
diff --git a/ui/src/utils/wildCardSearch.ts b/ui/src/utils/wildCardSearch.ts
deleted file mode 100644
index 5a002239..00000000
--- a/ui/src/utils/wildCardSearch.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export default function wildCardSearch(
- list: Array>,
- input: string,
- specifyKey?: string
-) {
- const searchText = (item: Record) => {
- for (const key in item) {
- if (item[specifyKey ? specifyKey : key] == null) {
- continue
- }
- if (
- item[specifyKey ? specifyKey : key]
- .toString()
- .toUpperCase()
- .indexOf(input.toString().toUpperCase()) !== -1
- ) {
- return true
- }
- }
- }
- const result = list.filter((value) => searchText(value))
- return result
-}
diff --git a/ui/src/views/index.ts b/ui/src/views/index.ts
deleted file mode 100644
index 6d75b4ae..00000000
--- a/ui/src/views/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import Views from './Views'
-
-export default Views