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 \n {`${sqlIndex_cmp_msyfmegt_jkkn3 + 1} / ${sqlRows_cmp_msyfmegt_jkkn3.length}`}\n \n \n \n ) : null}\n {sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 ? : null}\n {(sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3 && sqlCanUpdate_cmp_msyfmegt_jkkn3) ? : null}\n {sqldelete_cmp_msyfmegt_jkkn3 && sqlCanDelete_cmp_msyfmegt_jkkn3 ? : null}\n {sqlselect_cmp_msyfmegt_jkkn3 && sqlCanSelect_cmp_msyfmegt_jkkn3 ? : 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) => )}\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 \n })}\n \n ))}\n \n
{(column === \"value\" ? \"Value\" : column)}
{text}
\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 \n {`${sqlIndex_cmp_msyfmegt_jkkn3 + 1} / ${sqlRows_cmp_msyfmegt_jkkn3.length}`}\n \n \n \n ) : null}\n {sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 ? : null}\n {(sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3 && sqlCanUpdate_cmp_msyfmegt_jkkn3) ? : null}\n {sqldelete_cmp_msyfmegt_jkkn3 && sqlCanDelete_cmp_msyfmegt_jkkn3 ? : null}\n {sqlselect_cmp_msyfmegt_jkkn3 && sqlCanSelect_cmp_msyfmegt_jkkn3 ? : 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) => )}\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 \n })}\n \n ))}\n \n
{(column === \"value\" ? \"Value\" : column)}
{text}
\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, -}) => ( -
- - {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 ( -
-
- {text && {text}} -
- ); -}; - -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 */} - -
- ); -}; - -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 ( -
- - - -
-

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