From 1e9024e5a2d198bfab23247eb07e63caf7a7b1a3 Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Mon, 27 Apr 2026 12:31:09 +0000 Subject: [PATCH] Let admins edit role names + descriptions in both languages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original task (#88): The Roles tab in the Group editor displays each role's Arabic and English descriptions, but admins had no UI to edit them — any tweak required a developer. The edit dialog also didn't allow updating the role's name. Make role name + bilingual descriptions editable from the admin Roles panel and persist via PATCH /api/roles/:id. Changes: - lib/api-spec/openapi.yaml: UpdateRoleBody now accepts an optional `name` (minLength 1). PATCH /roles/{id} summary updated to "Update role name and descriptions (admin)" and documents 400 (invalid request) and 409 (name already taken) responses. Regenerated lib/api-zod. - artifacts/api-server/src/routes/roles.ts (PATCH /roles/:id): * Accept and validate `name` with the same rules as create (`/^[a-z][a-z0-9_]*$/i`, uniqueness check that ignores the role's own row). * Refuse to rename system roles (isSystem=1 or one of admin/user/ order_receiver) -> 400 with `code: "system_role_rename"`. * Returns 409 on name collision. - artifacts/tx-os/src/pages/admin.tsx (RolesPanel edit dialog): * Edit form now includes a Name input (data-testid="role-edit-name") prefilled from the role. * Name input is disabled for system roles, with a dedicated hint string. * Save is disabled when name is empty; 400 errors with `code: "system_role_rename"` show a system-role-specific toast, other 400s show the invalid-name toast, and 409 shows name-taken. - artifacts/tx-os/src/locales/{en,ar}.json: * Replaced the now-misleading `editHint` ("Role names cannot be changed") with `editSystemNameHint` (only shown for system roles). * Removed "Cannot be changed later" from `nameHint`. * Added `errorSystemRename` for the system-role rename toast. Validation: - pnpm typecheck passes. - Playwright e2e (testing skill) created a role, renamed it + updated both descriptions, verified the new name + descriptions appear on the role card and in the Group editor's Roles tab, confirmed empty name disables Save, and cleaned up via delete. (Test agent flagged one stale snapshot on the system-role disabled-name check; the implementation gates on r.isSystem from the API and is also enforced server-side.) Replit-Task-Id: 01dbc785-03e0-4714-b9d5-f0dff6de9a56 --- artifacts/api-server/src/routes/roles.ts | 25 ++++++++++ artifacts/tx-os/public/opengraph.jpg | Bin 25865 -> 25939 bytes artifacts/tx-os/src/locales/ar.json | 7 +-- artifacts/tx-os/src/locales/en.json | 7 +-- artifacts/tx-os/src/pages/admin.tsx | 47 ++++++++++++++++-- .../src/generated/api.schemas.ts | 2 + lib/api-client-react/src/generated/api.ts | 4 +- lib/api-spec/openapi.yaml | 17 ++++++- lib/api-zod/src/generated/api.ts | 3 +- 9 files changed, 97 insertions(+), 15 deletions(-) diff --git a/artifacts/api-server/src/routes/roles.ts b/artifacts/api-server/src/routes/roles.ts index 6fab3d68..2b66232a 100644 --- a/artifacts/api-server/src/routes/roles.ts +++ b/artifacts/api-server/src/routes/roles.ts @@ -77,6 +77,31 @@ router.patch("/roles/:id", requireAdmin, async (req, res): Promise => { return; } const updates: Partial = {}; + if (parsed.data.name !== undefined) { + const name = parsed.data.name.trim(); + if (name !== existing.name) { + const PROTECTED_NAMES = new Set(["admin", "user", "order_receiver"]); + if (existing.isSystem || PROTECTED_NAMES.has(existing.name)) { + res.status(400).json({ error: "Cannot rename a system role", code: "system_role_rename" }); + return; + } + if (!/^[a-z][a-z0-9_]*$/i.test(name)) { + res + .status(400) + .json({ error: "Role name must start with a letter and contain only letters, numbers, or underscores" }); + return; + } + const taken = await db + .select({ id: rolesTable.id }) + .from(rolesTable) + .where(eq(rolesTable.name, name)); + if (taken.some((r) => r.id !== id)) { + res.status(409).json({ error: "Role name already taken" }); + return; + } + updates.name = name; + } + } if (parsed.data.descriptionAr !== undefined) updates.descriptionAr = parsed.data.descriptionAr; if (parsed.data.descriptionEn !== undefined) updates.descriptionEn = parsed.data.descriptionEn; if (Object.keys(updates).length > 0) { diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index ec995ed15f1f6811adbae109a2e7e7b025b8b450..c6bb5d01c3a21ed3597a4584818a0b77f014b489 100644 GIT binary patch literal 25939 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 LN-9C&s~`Ub;nW<` literal 25865 zcmeFZcU%-n*C^b8U;I4d@`%q^I_YSE;|cI% zgWMoZ=q_}J{Qlq^6z4Puy152HRDb^6X8r<#%Kw0%i@krhT~37{x~C9S*7A4T-!ysj z!1ci)x?`ZGw6cPrKGq^F>wr#Scsu>)QMY)D0}eiRBys$)@COu|S_od>SZ@eEudqAtHjzBUtC4hkS z2l}7L!SN734Y(#uPkt%|1t1R<74=~c3VKSa^A~;*LTWNH$wwZe7Jjfu!}W^6NxY!JNF5Ctweyzh9C^DD(zX24AM;LmcDe%*IjZ za;0HJm^eGNQK$k%T`o}t0}{eE=ZhUzDbr>BPX&q=$Qb<&p`6F{p*i_7XBE)rbi$Z8 z@#cJd^@ugjyp%hsh_DlUElR#0sY72g#=f9NMUuJuM>#$zfca#>8aYQuy2tuwWbUQN zD8-D+c8VDak-1EY8OGL{h{%TqtRGcb?UlKPaZ??>qCEI?)v5MjoK1(X>P34hUsWE~ zh8Oq_rT9Wmk!)oaJhSnvGP{6PgM)30$~TgQxQKb>xB3JRJetK$$U@dYlAt1SVafJQPP6b?sAA%8?%h|K4pP*X5a zY;(ofNuA)k)9NE5aXd__XLm^3C;E&_Li{I}&k+|tugWDJKv8~vdCS(HlH2D3pTJV! z*;(<%SCQ98_vTex%C8Ajb?J4p+dK31$M45!O@(*{(C-^{`Z)K=T#bSu*BjVN$ATPP z*S*xSS7GdNL(LD8nzZTKON8 z8J8_OCm!L+NTHCyd-_6``n}t0$;HX985zz&NJeT3M(a#-M5q|kWo;ca{cjk7)#8%& z#!}YgEDI!G>4`hSqMcS^ENLyu$QL9#uDrCghU@}!b%S%Z<^u9MTxoe}skL{qTXW7m z)jy7tG3Wb}lmEK#X-2NpLuwvYbj`Vd19CPV`aH!bDRp8#UbefVm|>^rc-a7Og7!To zL}V^&gU4kPc#Ml!5m=g6km^@ZaD3$VX%%@I}H8=$Aoy$N(d^kCy zjhtJmZV0-&6105xfk&X`7IUvjbkCOi=9-DZ=)7q<|IGu)E1K=?x^lq9AsxSW*aTxM z788T1ok6X9D`Qu6=B*;zEunEihb`{DJ?z(2nDV;URg;cpS+;FjH5213qXVeK-;G!> zW3>9Vux*(+r*=8+;g)Jo&E4b7^3M!bJodyrn^FXyq%g6?-xC;k@i}&O#X>N-w)NNz zZ}#*czmpq!iOJr>m`anodDD{8rpj^oons``susQps^Y(5G@$#ZA*wD2Rzmt^8VY?) zz4rJt6q?r|l&YW07^!<%oAEaV-E?Gp=ly)vP<*6ecqxkx`UIXDg+g`ee_0hfr;lgi z%tN8_r-A7I_}-lPGj#foBbg|RJ2>&0e23&Zs&?lvN|BYYT`as%DEB05WDe1Q>5dpT z5@-PM#nWuLJ{5~`Yx0zEedpf{G=ZN1wdijR7>SIwC#Sb=YiH{oYY=rk`e z!gcXA@ag91+N)^W6@Mr}<{helJmH)e&2$ws5~8?$4mx)@Mp+#vwxUS;JVvbj&}ZS<)KP5D)DH5D1`Ax!wTM z=F^*>283rop;tNcl1p!+FLb`v>NOvLKWL5mSYvifHXDO zed>H_C&rQigeo1GAb{r96r;avB7o)^;lYSywkH6Zrr)s#G)=!VlN_y6&wj@Jh&Rib^s3UBC! z>zWZu=wJl2#v58DbciM78rYQ>6e!?I4JXTLkxS}feN<5fLwmGlhWxJ^ zVd-7L3~8Z^&?~$=0|j;PZE}YksDz}geIhmZ6}5gRB9!kVnPl>Y;SG_Qn9I5deT)bK z11u7?wziHP*GC@(@*%KGe?~skFXrQkdB}J{@p&tkZ2`~k zi7fcGL~8Q}d%$R7`gjz{0M8_(Ul@a`2lj?GR>+`0QNX-Pm!C0b(x9NrUMyQWo_x^> zT+p>=%;INk$Ad1w6xEyKtTM?&5{}8TXEX;4*=4ApeIHM6C!7Z4hBA~Z_~%H0M+iX5 z$ESk#XmxeISyG}-Xq-aXDj?cYf4xuc1pAXR2GU2JlGmU>BEncHuQ5`J(QD`+5c;q6 z8*z4k;3Y#&9FKQURfK`FI8N(K06lb7kz3-!R%;r-V=fh+u8Q z;MNm=Kb~r(ON<3WXuBJ2h-t8Jsi-%&6h6{#C!#BMfhiryr1*+Sl&GJfbDI%K&G74e zs%wThKp+82#6)=Td!S;n4qrE)5d{=3P{OFbLq^iOUvE`b?{-AGAJh?h8;{dXXH58d zpJ*opWRo$6c)wKeRRS(k8>jn1F_N#rrj$kd6p+#%mKiz{QDC*{A0AsRkKm6Gv~69t z4nnU-lt^~0mK;F*q^9}kt|6E>zUk$t_`5ZV#?(`sr%u2g9lqXb0_u^PR*y+&^%4AlM|VPy2d2T_t8#RKpVpa#8wCRGWr?k^xFjxnv|zA*qXZ(uDBGy;KP{HZzXXFSRcS`6-`Khx6=xI6Cj-$XvUKa z`l+_=e8L!Q8f2CopRT=wt`3Um1b9S5zo3i%x}rT}RlQZ|#dop%UhT~)y6H*gReHDT zoka9M0}d*^h|`@BLv^r-7&f&MK{s4iKA^2->g zhiXaQGR_9XughLrD+6ol+WV0s{V&Qb$5+vW>^Nsk;4`lD$X6N~6bjnV%I~RX6Ck>2 zNE@znYl zE;PM{{;!=mTp1c>$WvUwl=Vv5^yUJ{+qcQDd{j7++uj;r?zRykjW7*DPGT$tWQpjv ztN^|YEBX7xlb(rhWt8m^h0&))6(cjl7?B#t^J%F-LhVGuyI$b(0cToe+5&MdAuGS1 zpT=G%0Z2djdokwJo0sc@kA*}L$+8W)fkq4FcJN8{3xrYNDPnw`fW@!r@QY<>=6q`8 zZMuiFIXUA#JUlj0=JFucQ?-X@w>hxfDJ3QPNpx*pvcBc&s5>4$`9$J099O(I+i%(p z>uQR%$9*4}zS?0`d;s03Sof0q(h{BBG}Y*p9`M3HpS@a6+c)WI(roeUpt3|=3WvCy z8jG~4X-ag&Of*)&dFcEiKDXKIYJ`M~+O&ld9CMM=rVT}r4vALXkaw>VViBDQVe%Oo zXc!`~XDbh^EKfeWFf};cES@>eT_4$v4v{s&-KY#wS{Nc#yt87C6U-JAwTR9iZ}zgB zw^+59Ku7tMlx^K=P7DcY>0Bo6sYtK36wX`plODaixhFDdHosV4ZEY_d(Xdq>>)Gd6 zwkL*p!a7J9DtG5EPkMT!hBk!)gZ4RwUWjsv9wE;FX&@LGsGp*OGc=g!H5gFAcy6WE zPNmM=I~2}MvKQtlrwPzt?9y>`m?!wKj$BVVRG zt5V(%BXr0SbS*ah<`NutVF)LQix|apv5I~RrcsxKemfynjH10uH#%xKcB!fmX`xbO z={2boY5p;vH734*D-WTqkoWw2iEfF3-5K3{v6%3#LpL&^o-DaPWflk{BdQZvM4T=^ zGND2BkUgwQf%)vgftyfxRE)huD{wgtf3SrZ%l%w%`~5SasL=LPajlPG-s$rET0HVD z9c;7JaeDJUYyn=#r=8=CE$M<%b-I~&K^FhPfEkyT%^Eo-vvx0o%GDRX-di_nw+9G+ zl=hkAuUX?|v4VA^qV(jJnXnyk=82W11IRBsti-AXF*~Fev%_PG+g#YNUN=?W3z!-% zvHm^oYu~!VUcitqN6Gk1)5*Plafe0Ww_)$GMR5^9Zgs;cQgJ~Y%-9m!lGdi_WvTv) z+d?gZGw$9i953ph$#pr7zUq|{>XS{67F6>xQ&^ESd=@oea9JF~&6mKS0Q3#VtI(WT zPSlC8Oa}SemhQYf7~T`FQqS@RLutXMe_^C}kO?tT2j`p;KyU+v$$+N5ZOa&G{y_kt zpM$4HQ-@|C%}<>^2TmB}H5jjEW*l2XQMSY0K-5g!w}ne7%+peTi2LDTcMa13j68Bo zPpRJx(=d9yMcJiU0*#CvMTT}dac9^`HumQcZ7r?%mD(FDP4cfJtj*Ih#hdH-n9_9a z6Ag(t9B?14y6lla#EXfxpaz1h0KOk^C&{0-i{B-o-kt|YKGCY+e!VS_`$HBG%Oe5< zpVn#hhIl_$-}ZT4OacxSp2wBR3IZl@Uf$mlG>kPE71}b}Qa`q3wokFNWxcUsY3+)> zO@pC4#dCs}>vTr@Z;!)5gL&v$dr=YY^6tT0kAIoeI0yb9YHEnOl~tpi5=Frve}|6} z&2^0n{8*oA0|O>@9`TT@Bd;+s=p$|e!$lLt$CrnoKnB6Z)Mb8^-NKj4+Lpte6H9Ze*AtM$srHS3kzm?ut9Q?23U- zY?rPlpQuP_FDf>b`o3c#QrRFA*V`D22a*A(^cB}j)U_A07fS#pf4~i=o2Coor2deK zWH}Mbt2ciWgZc^x38+k-nsw%Dmgqds3)f8((M=cA$P4XM&=!(+N25-s-$~6&vuP*E zi`pXIi>Cet{dVdiXIA(*Dv0WsXxeWHj2GysZ!0*nIq7LSRjCM#t}Od3w4J2iri~TF zq;ua({Z%-kp-yhQgiF);$g4W!2WEMgZ@T8u1vXt*c|93FbUV4d8zRvyXnG z13apnPJ~YQT+ZV>hfq=6oF&2r(R=D$mSOKHp3m~S!eyziRSWB>9@0c5ss`i5tEn$o zwE~;^ufmaxtTZ0C(`zNf9Fi|4rzxZ0uB1^+4$(69iA^qdoLfg5O1Qd|0^oTzxbdU%} zYSC19u>5U#Z5CFF7!7*X+Wyic463R=kivgyFHs46#;t@)6Q$xxK#C^V-xkRdPW6$eJmNbQqLN z+f`s)2pvlgq`$Sy4~2wu5_WIy4E6X4-Ye}J*fb8(;#l9S8X5>)r#D zexO$fv$M$i5dgnl^sY&U&8oFw&+GIx9-Tx!Eh`?swrCR%XSZ;^NYg=PpN5J;pkMdE`CUs{oW%Ia)X(WJxIf9HV?{vpoyXH~LQ&?WMHzW84L@HKczk-G79mLcMuujrG`o_xX;Dhz-6K2o0JRoYa?nPOYSISm#aipY#(Iur>pc|;oi!AA$sX}UUC z6rp-}pO)j%3J@3THOXvUH=~uV$?jUM z;b77ojR?34m-g7_POe%J8yrD)SY4{^ZEo;=X$Nv>Ofp?5~kII&A-RZvq&~T?(7HU2uTY<+?7fv zqUwWyODm+gV2Ew|2Mcl6B6n*AhilWH(u{(b-anYBgWtRRW@nM7bT5e&a8st8t`X+` z$9#SyJJ~qt>ZXg|BFpZP)OI`CS@?-Jblc|wD|JcS_K2S>j8wBaZ1d4?N`WxzlqPC+ z_S(gk*uJsW#`Thg7)^Eyi2AyGy+o@wB0y|Gz{Bp4&+IxC)40ffk!4N{18XS!cO(O= zg5kwZYTy!eX!gwHiRKSAk+Q=A$J$AVg`qU|g*|g%yEn4GhWG~PCz$Zo?5rv*3}t}s zWkV*cZLbW-j?_o%`&FX9g?3?h&P=sWwLQ5d+7+$DCCVj?u@!x6E85PvN82uu(SbIw z6z#&WilnP@O*Lag$pB4Qx^lYGmGG`vtGc_lHvHw@*DVsV@blh5Pk=}E`4uD6;d?TXM=g!=M zOK)aYSV&3=7xxTS$B$a4u-8a%$T_acEv+saHKtsV3d*l5`aWG1D823QdZzNG(AyVk zY?2}kGj6A`3w>AT3EMAcWfOd~Qk+$`Y8{%=39l-gyaPTbwfJ`J^p$rP_$GfXPzegQ z>`g|l?J+-G53*h5Smu!LV^8b|B)puBh_&cgW83VxDmKx$A@bo%0E?>69A>mD=##Mt zwvRn-9NZG(xL(HAZ@fqARZ``oMhj22TUKY*V%rk_Fi9AgUkF6!SMG2A`o>~6G5-LH z^iOZX7LCkua8~iQW7=H{K9hWV~YEnRwO;(mAXL-R%o1v1|%K_S7(2xnGFR znj9v?ep#HBe7mJmcyUOyb08t;!PlTqgW4X(HO8}MoI8FnS+(TRR_ku}WZ3qLYG=`< zD|Kj-0pGd)6}g&axz`nMUo1Pnnhm6N*xKAScDlK2eYw(^-+wtsukw2GGdV)FT>f%& zqx!YoggLd2*}#seZ8)rVyeT9km*g+2mJ+gl2~+vS3+H^j*~0Z*KuhKRhm%e@l(YeN zmv?KQ?MGa!UgKz5-oLbc^EL=#U~d1*Jun!65bJtbV z_~sH-OYFtp7!osc?ZuQ0-T?O#$xFQ7W?)FPG}q2Iu*?Y-(Y_P@CbX0$m|j6!_fHKw zp6Bf-l&y}IG8Z}+9`i^Q{=7YpZ>l|1Ed4$ZF5O zFEl|_l=CkDJh`PR%EP@Xbtj^%_;V&j605!9bCCWKJxmCYD~d#xf-XB+k9lwFy_9H` zXNKp8!hW`}N^bbbzJtweCj98aDmio1qfoslq&g7(dwO!^f9xQ106BNp4b)!PA0qd- z{wzGGZ*6hI`6Ec$k#VCRq;stAImq-a$<1wl1CRY?L)fb|qVwEU-w7-Ok1As&qv$$9 zQSdJmuYaI;aEL-`{P14qxEb>KoihuEK-fF8!yBOHtwU6aN4;AAo0kWFcHgKxygh0D z2f7x&BlQ2d1Fj{?*Gf{o`b(dad(t#R$v`1xEf~Je4)7BCIpypcIpR< z7~WP?f+Wrz(>P~G6Pnk4%0@o{sROc12;&R)t4#Q^;_ejE{LxNw(J+C>(YROHD}vg# z03Lp^om9Yj!}?y0V7X{=vhf~BIbukn@!=$3DmXvbzG*iXPOw)Z-ktJ%hPmZX zoN&mRaq{Wh^6C7UXD=wmDPmag2_ShwmE`Bd@qKCk;+n2ZIhalyKxg*Y{ChFHcP}Zm z(0!>z3wD(e=!hUj?NF;35iCrm(Z4HHY{}t4Oma9MI*#bJ@B^ricLUS6 z0?q1z?w&YHj%l9yiH2(BnE*~Py~mwj6NhUo#wm^)=*L(n0q=047?(r3#(y4Hz@0_J zmwqEF0h7Y`LyD8@H$7QmB@T1qHeDUSN#~mr4|~(5WozdsrkX`e=er`_$Qv7*e*MHr z9SRmCgJK$lLUEN+$Y*Higr-f|UbZn{L_RbTuxDge<|^b=naV+R0g68YH=*2NnJE4ntL*Gl1}s&jUk9qODWHjiOXM z32mtlI}8yQ6l2`aU~F1NK%T{p)>9_?_z{U=#Wg>{ixc?Q7j4|OUi1YznbxLNL1(

oHRlEt3RT^^Zn?F^ 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 F{$F{PCX@gG diff --git a/artifacts/tx-os/src/locales/ar.json b/artifacts/tx-os/src/locales/ar.json index d1d61b0d..fb5e9a80 100644 --- a/artifacts/tx-os/src/locales/ar.json +++ b/artifacts/tx-os/src/locales/ar.json @@ -421,14 +421,14 @@ "editRole": "تعديل الدور", "name": "الاسم", "namePlaceholder": "مثال: content_editor", - "nameHint": "حروف وأرقام وشرطات سفلية فقط. لا يمكن تغييره لاحقاً.", + "nameHint": "حروف وأرقام وشرطات سفلية فقط.", "descriptionAr": "الوصف (عربي)", "descriptionEn": "الوصف (إنجليزي)", "system": "نظامي", "empty": "لا توجد أدوار بعد.", "edit": "تعديل الدور", "delete": "حذف الدور", - "editHint": "لا يمكن تغيير اسم الدور. يمكنك تحديث الأوصاف بالعربية والإنجليزية.", + "editSystemNameHint": "لا يمكن تغيير اسم الأدوار النظامية.", "deleteTitle": "حذف الدور \"{{name}}\"؟", "deleteEmptyBody": "هذا الدور غير مُعيَّن لأي مستخدم. هل تريد المتابعة؟", "deleteConflict": "هذا الدور قيد الاستخدام ولا يمكن حذفه.", @@ -437,7 +437,8 @@ "usersCount": "{{count}} مستخدم", "groupsCount": "{{count}} مجموعة", "errorNameTaken": "يوجد دور بنفس الاسم.", - "errorInvalidName": "يجب أن يبدأ اسم الدور بحرف ويحتوي على حروف وأرقام وشرطات سفلية فقط." + "errorInvalidName": "يجب أن يبدأ اسم الدور بحرف ويحتوي على حروف وأرقام وشرطات سفلية فقط.", + "errorSystemRename": "لا يمكن تغيير اسم الأدوار النظامية." }, "groups": { "searchPlaceholder": "ابحث في المجموعات...", diff --git a/artifacts/tx-os/src/locales/en.json b/artifacts/tx-os/src/locales/en.json index 1f122694..da855c1e 100644 --- a/artifacts/tx-os/src/locales/en.json +++ b/artifacts/tx-os/src/locales/en.json @@ -418,14 +418,14 @@ "editRole": "Edit Role", "name": "Name", "namePlaceholder": "e.g. content_editor", - "nameHint": "Letters, numbers and underscores only. Cannot be changed later.", + "nameHint": "Letters, numbers and underscores only.", "descriptionAr": "Description (Arabic)", "descriptionEn": "Description (English)", "system": "System", "empty": "No roles yet.", "edit": "Edit role", "delete": "Delete role", - "editHint": "Role names cannot be changed. You can update the bilingual descriptions.", + "editSystemNameHint": "System role names cannot be changed.", "deleteTitle": "Delete role \"{{name}}\"?", "deleteEmptyBody": "This role isn't assigned to anyone. Continue?", "deleteConflict": "This role is still in use and cannot be deleted.", @@ -434,7 +434,8 @@ "usersCount": "{{count}} users", "groupsCount": "{{count}} groups", "errorNameTaken": "A role with that name already exists.", - "errorInvalidName": "Role name must start with a letter and contain only letters, numbers, or underscores." + "errorInvalidName": "Role name must start with a letter and contain only letters, numbers, or underscores.", + "errorSystemRename": "System role names cannot be changed." }, "groups": { "searchPlaceholder": "Search groups...", diff --git a/artifacts/tx-os/src/pages/admin.tsx b/artifacts/tx-os/src/pages/admin.tsx index ec4cb79c..17f8d83a 100644 --- a/artifacts/tx-os/src/pages/admin.tsx +++ b/artifacts/tx-os/src/pages/admin.tsx @@ -3162,7 +3162,8 @@ function RolesPanel() { const [createOpen, setCreateOpen] = useState(false); const [createForm, setCreateForm] = useState({ name: "", descriptionAr: "", descriptionEn: "" }); const [editingId, setEditingId] = useState(null); - const [editForm, setEditForm] = useState({ descriptionAr: "", descriptionEn: "" }); + const [editingIsSystem, setEditingIsSystem] = useState(false); + const [editForm, setEditForm] = useState({ name: "", descriptionAr: "", descriptionEn: "" }); const [deleteTarget, setDeleteTarget] = useState<{ id: number; name: string } | null>(null); const [deleteConflict, setDeleteConflict] = useState<{ userCount: number; groupCount: number } | null>(null); @@ -3211,9 +3212,17 @@ function RolesPanel() { ); }; - const beginEdit = (role: { id: number; descriptionAr?: string | null; descriptionEn?: string | null }) => { + const beginEdit = (role: { + id: number; + name: string; + isSystem: boolean; + descriptionAr?: string | null; + descriptionEn?: string | null; + }) => { setEditingId(role.id); + setEditingIsSystem(role.isSystem); setEditForm({ + name: role.name, descriptionAr: role.descriptionAr ?? "", descriptionEn: role.descriptionEn ?? "", }); @@ -3221,10 +3230,13 @@ function RolesPanel() { const handleEdit = () => { if (editingId == null) return; + const name = editForm.name.trim(); + if (!name) return; updateRole.mutate( { id: editingId, data: { + ...(editingIsSystem ? {} : { name }), descriptionAr: editForm.descriptionAr.trim() || null, descriptionEn: editForm.descriptionEn.trim() || null, }, @@ -3235,7 +3247,19 @@ function RolesPanel() { setEditingId(null); toast({ title: t("common.success") }); }, - onError: () => toast({ title: t("common.error"), variant: "destructive" }), + onError: (err: unknown) => { + if (err instanceof ApiError && err.status === 409) { + toast({ title: t("admin.roles.errorNameTaken"), variant: "destructive" }); + return; + } + if (err instanceof ApiError && err.status === 400) { + const code = (err.data as { code?: string } | null)?.code; + const key = code === "system_role_rename" ? "admin.roles.errorSystemRename" : "admin.roles.errorInvalidName"; + toast({ title: t(key), variant: "destructive" }); + return; + } + toast({ title: t("common.error"), variant: "destructive" }); + }, }, ); }; @@ -3401,7 +3425,20 @@ function RolesPanel() {

{t("admin.roles.editRole")}

-

{t("admin.roles.editHint")}

+
+ + setEditForm({ ...editForm, name: e.target.value })} + className="bg-white/70 border-slate-200" + placeholder={t("admin.roles.namePlaceholder")} + disabled={editingIsSystem} + data-testid="role-edit-name" + /> +

+ {editingIsSystem ? t("admin.roles.editSystemNameHint") : t("admin.roles.nameHint")} +

+