@aiquants/auth-react-router 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/admin.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var G=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var $=(r,i)=>{for(var t in i)G(r,t,{get:i[t],enumerable:!0})},K=(r,i,t,l)=>{if(i&&typeof i=="object"||typeof i=="function")for(let c of F(i))!W.call(r,c)&&c!==t&&G(r,c,{get:()=>i[c],enumerable:!(l=T(i,c))||l.enumerable});return r};var q=r=>K(G({},"__esModule",{value:!0}),r);var Y={};$(Y,{AllowlistView:()=>N,AuthUserAdminAppView:()=>X,GroupMembersView:()=>E,GroupsView:()=>P,UserAdminShell:()=>z,UsersView:()=>M,createInMemoryUserAdminStore:()=>O,createUserAdminApp:()=>H,defaultUserAdminLabels:()=>w,jaUserAdminLabels:()=>j,resolveUserAdminLabels:()=>R});module.exports=q(Y);var w={title:"Users & Groups",heading:"\u{1F464} User administration",tabs:{users:"\u{1F464} Users",groups:"\u{1F465} Groups",groupMembers:"\u{1F517} Members",allowlist:"\u2709\uFE0F Allowlist"},usersView:{title:"Users",searchPlaceholder:"Search by name or email...",addUser:"\uFF0B Add user",editUser:"Edit user",id:"ID",displayName:"Name",email:"Email",status:"Status",active:"Active",inactive:"Inactive",actions:"Actions",edit:"Edit",activate:"Activate",deactivate:"Deactivate",empty:"No matching users",save:"Save",cancel:"Cancel"},groupsView:{title:"Groups",searchPlaceholder:"Search by group name or key...",addGroup:"\uFF0B Add group",editGroup:"Edit group",id:"ID",groupKey:"Group key",name:"Name",description:"Description",memberCount:"Members",status:"Status",actions:"Actions",edit:"Edit",delete:"Delete",confirmDelete:"Delete this group?",noDescription:"No description",groupKeyPlaceholder:"e.g. dev-team",namePlaceholder:"e.g. Development team",empty:"No groups registered",save:"Save",cancel:"Cancel"},groupMembersView:{selectGroup:"Select a group",selectPrompt:"Select a group to manage its members",actionAdd:"Add",actionRemove:"Remove"},allowlistView:{title:"Sign-in allowlist",addEntry:"\uFF0B Add pattern",pattern:"Pattern (e.g. @example.com or user@example.com)",type:"Type",emailType:"Exact email match",domainType:"Domain match",emailTypeShort:"Email",domainTypeShort:"Domain",description:"Description",actions:"Actions",readOnly:"Read-only",delete:"Delete",empty:"No allowlist entries registered",save:"Add",cancel:"Cancel"}},j={title:"\u30E6\u30FC\u30B6\u30FC\u30FB\u30B0\u30EB\u30FC\u30D7\u7BA1\u7406",heading:"\u{1F464} \u30E6\u30FC\u30B6\u30FC\u7BA1\u7406",tabs:{users:"\u{1F464} \u30E6\u30FC\u30B6\u30FC\u4E00\u89A7",groups:"\u{1F465} \u30E6\u30FC\u30B6\u30FC\u30B0\u30EB\u30FC\u30D7",groupMembers:"\u{1F517} \u30B0\u30EB\u30FC\u30D7\u30E1\u30F3\u30D0\u30FC",allowlist:"\u2709\uFE0F \u30A2\u30AF\u30BB\u30B9\u8A31\u53EF\u30EA\u30B9\u30C8"},usersView:{title:"\u30E6\u30FC\u30B6\u30FC\u7BA1\u7406",searchPlaceholder:"\u8868\u793A\u540D\u3084 Email \u3067\u691C\u7D22...",addUser:"\uFF0B \u30E6\u30FC\u30B6\u30FC\u8FFD\u52A0",editUser:"\u30E6\u30FC\u30B6\u30FC\u306E\u7DE8\u96C6",id:"ID",displayName:"\u8868\u793A\u540D",email:"\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9",status:"\u30B9\u30C6\u30FC\u30BF\u30B9",active:"\u6709\u52B9",inactive:"\u7121\u52B9",actions:"\u64CD\u4F5C",edit:"\u7DE8\u96C6",activate:"\u6709\u52B9\u5316",deactivate:"\u7121\u52B9\u5316",empty:"\u8A72\u5F53\u3059\u308B\u30E6\u30FC\u30B6\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",save:"\u4FDD\u5B58",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"},groupsView:{title:"\u30E6\u30FC\u30B6\u30FC\u30B0\u30EB\u30FC\u30D7\u7BA1\u7406",searchPlaceholder:"\u30B0\u30EB\u30FC\u30D7\u540D\u3084\u30AD\u30FC\u3067\u691C\u7D22...",addGroup:"\uFF0B \u30B0\u30EB\u30FC\u30D7\u4F5C\u6210",editGroup:"\u30B0\u30EB\u30FC\u30D7\u306E\u7DE8\u96C6",id:"ID",groupKey:"\u30B0\u30EB\u30FC\u30D7\u30AD\u30FC",name:"\u30B0\u30EB\u30FC\u30D7\u540D",description:"\u8AAC\u660E",memberCount:"\u30E1\u30F3\u30D0\u30FC\u6570",status:"\u30B9\u30C6\u30FC\u30BF\u30B9",actions:"\u64CD\u4F5C",edit:"\u7DE8\u96C6",delete:"\u524A\u9664",confirmDelete:"\u3053\u306E\u30B0\u30EB\u30FC\u30D7\u3092\u524A\u9664\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B\uFF1F",noDescription:"\u8AAC\u660E\u306A\u3057",groupKeyPlaceholder:"\u4F8B: dev-team",namePlaceholder:"\u4F8B: \u958B\u767A\u30C1\u30FC\u30E0",empty:"\u30B0\u30EB\u30FC\u30D7\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093",save:"\u4FDD\u5B58",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"},groupMembersView:{selectGroup:"\u30B0\u30EB\u30FC\u30D7\u9078\u629E",selectPrompt:"\u30B0\u30EB\u30FC\u30D7\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",actionAdd:"\u8FFD\u52A0",actionRemove:"\u9664\u5916"},allowlistView:{title:"\u30A2\u30AF\u30BB\u30B9\u8A31\u53EF\u30EA\u30B9\u30C8 (Allowlist)",addEntry:"\uFF0B \u8A31\u53EF\u30D1\u30BF\u30FC\u30F3\u8FFD\u52A0",pattern:"\u30D1\u30BF\u30FC\u30F3 (\u4F8B: @example.com \u307E\u305F\u306F user@example.com)",type:"\u7A2E\u5225",emailType:"\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u5B8C\u5168\u4E00\u81F4",domainType:"\u30C9\u30E1\u30A4\u30F3\u4E00\u81F4",emailTypeShort:"Email \u5B8C\u5168\u4E00\u81F4",domainTypeShort:"\u30C9\u30E1\u30A4\u30F3\u4E00\u81F4",description:"\u8AAC\u660E / \u5099\u8003",actions:"\u64CD\u4F5C",readOnly:"\u5909\u66F4\u4E0D\u53EF",delete:"\u524A\u9664",empty:"\u30A2\u30ED\u30EF\u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093",save:"\u767B\u9332",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"}};function R(r){let i=w;return r?{title:r.title??i.title,heading:r.heading??i.heading,tabs:{...i.tabs,...r.tabs},usersView:{...i.usersView,...r.usersView},groupsView:{...i.groupsView,...r.groupsView},groupMembersView:{...i.groupMembersView,...r.groupMembersView},allowlistView:{...i.allowlistView,...r.allowlistView}}:i}function O(r){let i=r?.users??[{id:1,email:"admin@example.com",displayName:"\u30B7\u30B9\u30C6\u30E0\u7BA1\u7406\u8005",isActive:!0},{id:2,email:"user1@example.com",displayName:"\u958B\u767A \u592A\u90CE",isActive:!0},{id:3,email:"user2@example.com",displayName:"\u55B6\u696D \u82B1\u5B50",isActive:!0}],t=r?.groups??[{id:1,groupKey:"dev-team",name:"\u958B\u767A\u30C1\u30FC\u30E0",description:"\u30B7\u30B9\u30C6\u30E0\u958B\u767A\u62C5\u5F53\u30B0\u30EB\u30FC\u30D7",isActive:!0},{id:2,groupKey:"sales-dept",name:"\u55B6\u696D\u90E8",description:"\u55B6\u696D\u30BD\u30EA\u30E5\u30FC\u30B7\u30E7\u30F3\u62C5\u5F53",isActive:!0}],l=r?.groupMembers??[{id:1,groupId:1,userId:2},{id:2,groupId:2,userId:3}],c=r?.allowlist??[{id:1,pattern:"admin@example.com",type:"email",description:"\u521D\u671F\u7BA1\u7406\u8005"},{id:2,pattern:"@example.com",type:"domain",description:"\u793E\u5185\u30C9\u30E1\u30A4\u30F3"}];return{async listUsers(){return[...i]},async createUser(s){let o={id:Math.max(...i.map(n=>n.id),0)+1,email:s.email,displayName:s.displayName,isActive:!0};return i.push(o),o},async updateUser(s,o){let n=i.findIndex(a=>a.id===s);if(n===-1)throw new Error(`User not found: ${s}`);return i[n]={...i[n],displayName:o.displayName,email:o.email},i[n]},async toggleUserActive(s,o){let n=i.findIndex(a=>a.id===s);if(n===-1)throw new Error(`User not found: ${s}`);return i[n]={...i[n],isActive:o},i[n]},async listGroups(){return[...t]},async createGroup(s){let o={id:Math.max(...t.map(n=>n.id),0)+1,groupKey:s.groupKey,name:s.name,description:s.description,isActive:!0};return t.push(o),o},async updateGroup(s,o){let n=t.findIndex(a=>a.id===s);if(n===-1)throw new Error(`Group not found: ${s}`);return t[n]={...t[n],name:o.name,description:o.description},t[n]},async deleteGroup(s){t=t.filter(o=>o.id!==s),l=l.filter(o=>o.groupId!==s)},async listGroupMembers(){return[...l]},async addGroupMember(s,o){let n=l.find(p=>p.groupId===s&&p.userId===o);if(n)return n;let a={id:Math.max(...l.map(p=>p.id),0)+1,groupId:s,userId:o};return l.push(a),a},async removeGroupMember(s,o){l=l.filter(n=>!(n.groupId===s&&n.userId===o))},async listAllowlist(){return[...c]},async addAllowlistEntry(s){let o={id:Math.max(...c.map(n=>n.id),0)+1,pattern:s.pattern,type:s.type,description:s.description};return c.push(o),o},async removeAllowlistEntry(s){c=c.filter(o=>o.id!==s)}}}var _=new Map([["createUser","create"],["updateUser","update"],["toggleUserActive","update"],["createGroup","create"],["updateGroup","update"],["deleteGroup","delete"],["addGroupMember","create"],["removeGroupMember","delete"],["addAllowlistEntry","create"],["removeAllowlistEntry","delete"]]);function v(r,i){let t=r.get(i),l=Number(t);if(t===null||String(t).trim()===""||!Number.isInteger(l)||l<=0)throw new Error(`Invalid or missing "${i}"`);return l}function A(r,i){let t=String(r.get(i)??"").trim();if(t==="")throw new Error(`Invalid or missing "${i}"`);return t}function I(r,i){let t=String(r.get(i)??"").trim();return t===""?void 0:t}function H(r){let i=R(r.labels);async function t({request:s}){await r.guards.requireAccess(s,{action:"read",intent:"loader"});let n=new URL(s.url).pathname.split("/").filter(Boolean),a=n[n.length-1]??"users",[p,x,d,g,y]=await Promise.all([r.store.listUsers(),r.store.listGroups(),r.store.listGroupMembers(),r.store.listAllowlist(),r.getMyPermissions?r.getMyPermissions(s).catch(()=>({})):Promise.resolve({})]);return{segment:["groups","group_members","allowlist"].includes(a)?a:"users",users:p,groups:x,groupMembers:d,allowlist:g,myPermissions:y,labels:i}}async function l({request:s}){let o=await s.formData(),n=String(o.get("intent")??""),a=_.get(n);if(!a)return{ok:!1,error:`Unknown intent: ${n}`};await r.guards.requireAccess(s,{action:a,intent:n});try{return await c(n,o)}catch(p){if(p instanceof Response)throw p;return{ok:!1,error:p instanceof Error?p.message:String(p)}}}async function c(s,o){if(s==="createUser")return await r.store.createUser({displayName:A(o,"displayName"),email:A(o,"email")}),{ok:!0};if(s==="updateUser")return await r.store.updateUser(v(o,"userId"),{displayName:A(o,"displayName"),email:A(o,"email")}),{ok:!0};if(s==="toggleUserActive"){let n=String(o.get("isActive")??"");if(n!=="true"&&n!=="false")throw new Error('Invalid or missing "isActive"');return await r.store.toggleUserActive(v(o,"userId"),n==="true"),{ok:!0}}if(s==="createGroup")return await r.store.createGroup({groupKey:A(o,"groupKey"),name:A(o,"name"),description:I(o,"description")}),{ok:!0};if(s==="updateGroup")return await r.store.updateGroup(v(o,"groupId"),{name:A(o,"name"),description:I(o,"description")}),{ok:!0};if(s==="deleteGroup")return await r.store.deleteGroup(v(o,"groupId")),{ok:!0};if(s==="addGroupMember")return await r.store.addGroupMember(v(o,"groupId"),v(o,"userId")),{ok:!0};if(s==="removeGroupMember")return await r.store.removeGroupMember(v(o,"groupId"),v(o,"userId")),{ok:!0};if(s==="addAllowlistEntry"){let n=o.get("type")==="domain"?"domain":"email";return await r.store.addAllowlistEntry({pattern:A(o,"pattern"),type:n,description:I(o,"description")}),{ok:!0}}return s==="removeAllowlistEntry"?(await r.store.removeAllowlistEntry(v(o,"entryId")),{ok:!0}):{ok:!1,error:`Unknown intent: ${s}`}}return{loader:t,action:l}}var U=require("react-router");var b=require("react/jsx-runtime");function Q({label:r,summary:i}){return(0,b.jsxs)("span",{style:{display:"inline-flex",alignItems:"center",gap:"0.18rem",fontSize:"0.65rem",padding:"0.08rem 0.35rem",borderRadius:"9999px",backgroundColor:i.bgColor,color:i.color,border:`1px solid ${i.borderColor}`,fontWeight:600,lineHeight:"1.2"},children:[(0,b.jsxs)("span",{children:[r,":"]}),(0,b.jsx)("span",{children:i.text})]})}function z({children:r,renderHeader:i}){let t=(0,U.useLoaderData)(),l=t?.segment??"users",c=t?.labels??w,s=t?.myPermissions??{},o=[{key:"users",label:c.tabs.users,href:"/users"},{key:"groups",label:c.tabs.groups,href:"/users/groups"},{key:"group_members",label:c.tabs.groupMembers,href:"/users/group_members"},{key:"allowlist",label:c.tabs.allowlist,href:"/users/allowlist"}],n=Object.entries(s),a=n.length>0?n:[["user_admin",{read:!0,write:!0,delete:!0,allowedActions:["read","create","update","delete"]}]],p=d=>{let g=d,y=g?.allowedActions??[],S=g?.write??(y.includes("create")||y.includes("update")||y.includes("delete")),C=g?.read??y.includes("read");return S?{text:"\u7DE8\u96C6\u53EF",color:"#047857",bgColor:"#ecfdf5",borderColor:"#a7f3d0"}:C?{text:"\u95B2\u89A7\u306E\u307F",color:"#1d4ed8",bgColor:"#eff6ff",borderColor:"#bfdbfe"}:{text:"\u6A29\u9650\u306A\u3057",color:"#64748b",bgColor:"#f1f5f9",borderColor:"#cbd5e1"}},x=(0,b.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"0.4rem"},"data-testid":"user-admin-header-annotation",children:[(0,b.jsx)("span",{style:{fontSize:"0.75rem",backgroundColor:"#e0f2fe",color:"#0369a1",padding:"2px 8px",borderRadius:"12px",fontWeight:600},children:c.heading}),(0,b.jsxs)("div",{style:{display:"inline-flex",alignItems:"center",gap:"0.25rem",fontSize:"0.68rem",color:"#475569"},"data-testid":"user-admin-permissions-badge",children:[(0,b.jsx)("span",{style:{fontWeight:600,color:"#334155"},children:"\u3042\u306A\u305F\u306E\u6A29\u9650:"}),(0,b.jsx)("span",{style:{display:"inline-flex",alignItems:"center",gap:"0.18rem"},children:a.map(([d,g],y)=>(0,b.jsx)(Q,{label:d==="user_admin"?"\u30E6\u30FC\u30B6\u30FC\u7BA1\u7406UI":String(d),summary:p(g)},String(d||y)))})]})]});return(0,b.jsxs)("div",{"data-testid":"user-admin-shell",style:{width:"100%",minHeight:"100vh",backgroundColor:"#f8fafc",padding:"1.5rem",boxSizing:"border-box"},children:[i?i({title:c.title,annotation:x}):(0,b.jsxs)("div",{style:{marginBottom:"1.5rem",display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,b.jsx)("h1",{style:{margin:0,fontSize:"1.5rem",fontWeight:700,color:"#0f172a"},children:c.title}),x]}),(0,b.jsx)("div",{style:{display:"flex",gap:"0.5rem",marginBottom:"1.2rem",borderBottom:"2px solid #e2e8f0",paddingBottom:"0.5rem"},children:o.map(d=>{let g=l===d.key;return(0,b.jsx)(U.Link,{to:d.href,"data-testid":`user-admin-tab-${d.key}`,style:{padding:"0.5rem 1rem",borderRadius:"6px 6px 0 0",fontSize:"0.88rem",fontWeight:g?700:500,color:g?"#0284c7":"#64748b",backgroundColor:g?"#ffffff":"transparent",textDecoration:"none",borderBottom:g?"3px solid #0284c7":"3px solid transparent",transition:"all 0.15s ease"},children:d.label},d.key)})}),(0,b.jsx)("main",{children:r})]})}var f=require("react"),h=require("react-router");var e=require("react/jsx-runtime");function k({fetcher:r}){let i=r.data;return i?.ok!==!1||!i.error?null:(0,e.jsx)("div",{"data-testid":"user-admin-action-error",role:"alert",style:{padding:"0.7rem 1rem",borderRadius:"6px",border:"1px solid #fecaca",backgroundColor:"#fef2f2",color:"#b91c1c",fontSize:"0.85rem"},children:i.error})}function M(){let r=(0,h.useLoaderData)(),i=(0,h.useFetcher)(),t=r.labels??w,[l,c]=(0,f.useState)(""),[s,o]=(0,f.useState)(null),[n,a]=(0,f.useState)(!1),[p,x]=(0,f.useState)(""),[d,g]=(0,f.useState)(""),y=r.users.filter(u=>u.displayName.toLowerCase().includes(l.toLowerCase())||u.email.toLowerCase().includes(l.toLowerCase())),S=u=>{i.submit({intent:"toggleUserActive",userId:String(u.id),isActive:String(!u.isActive)},{method:"post"})},C=u=>{u.preventDefault(),p&&d&&(i.submit({intent:"createUser",displayName:p,email:d},{method:"post"}),x(""),g(""),a(!1))},V=u=>{u.preventDefault(),s&&(i.submit({intent:"updateUser",userId:String(s.id),displayName:s.displayName,email:s.email},{method:"post"}),o(null))};return(0,e.jsxs)("section",{"data-testid":"users-view-section",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[(0,e.jsx)(k,{fetcher:i}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[(0,e.jsx)("input",{type:"text",placeholder:t.usersView.searchPlaceholder,value:l,onChange:u=>c(u.target.value),"data-testid":"users-search-input",style:{padding:"6px 12px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",width:"260px"}}),(0,e.jsx)("button",{type:"button",onClick:()=>a(!0),"data-testid":"users-add-button",style:{padding:"6px 14px",borderRadius:"6px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.usersView.addUser})]}),(0,e.jsx)("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",overflow:"hidden"},children:(0,e.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.85rem",textAlign:"left"},children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{style:{backgroundColor:"#f8fafc",borderBottom:"1px solid #e2e8f0",color:"#475569"},children:[(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.usersView.id}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.usersView.displayName}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.usersView.email}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.usersView.status}),(0,e.jsx)("th",{style:{padding:"10px 14px",textAlign:"right"},children:t.usersView.actions})]})}),(0,e.jsxs)("tbody",{children:[y.map(u=>(0,e.jsxs)("tr",{style:{borderBottom:"1px solid #f1f5f9"},"data-testid":`user-row-${u.id}`,children:[(0,e.jsxs)("td",{style:{padding:"10px 14px",color:"#64748b"},children:["#",u.id]}),(0,e.jsx)("td",{style:{padding:"10px 14px",fontWeight:600,color:"#0f172a"},children:u.displayName}),(0,e.jsx)("td",{style:{padding:"10px 14px",color:"#475569"},children:u.email}),(0,e.jsx)("td",{style:{padding:"10px 14px"},children:(0,e.jsx)("span",{style:{fontSize:"0.75rem",padding:"2px 8px",borderRadius:"10px",backgroundColor:u.isActive?"#dcfce7":"#fee2e2",color:u.isActive?"#15803d":"#b91c1c",fontWeight:600},children:u.isActive?t.usersView.active:t.usersView.inactive})}),(0,e.jsxs)("td",{style:{padding:"10px 14px",textAlign:"right"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>o(u),"data-testid":`edit-user-btn-${u.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.78rem",cursor:"pointer",marginRight:"6px"},children:t.usersView.edit}),(0,e.jsx)("button",{type:"button",onClick:()=>S(u),"data-testid":`toggle-user-btn-${u.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.78rem",cursor:"pointer",color:u.isActive?"#b91c1c":"#15803d"},children:u.isActive?t.usersView.deactivate:t.usersView.activate})]})]},u.id)),y.length===0&&(0,e.jsx)("tr",{children:(0,e.jsx)("td",{colSpan:5,style:{padding:"2rem",textAlign:"center",color:"#94a3b8"},children:t.usersView.empty})})]})]})}),n&&(0,e.jsx)("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"user-create-modal",children:(0,e.jsxs)("form",{onSubmit:C,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[(0,e.jsx)("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.usersView.addUser}),(0,e.jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,e.jsx)("label",{htmlFor:"create-user-display-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.displayName}),(0,e.jsx)("input",{id:"create-user-display-name-input",type:"text",value:p,onChange:u=>x(u.target.value),required:!0,"data-testid":"create-user-display-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{marginBottom:"1.2rem"},children:[(0,e.jsx)("label",{htmlFor:"create-user-email-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.email}),(0,e.jsx)("input",{id:"create-user-email-input",type:"email",value:d,onChange:u=>g(u.target.value),required:!0,"data-testid":"create-user-email",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>a(!1),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:"\u30AD\u30E3\u30F3\u30BB\u30EB"}),(0,e.jsx)("button",{type:"submit","data-testid":"submit-create-user",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",cursor:"pointer"},children:"\u4F5C\u6210"})]})]})}),s&&(0,e.jsx)("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"user-edit-modal",children:(0,e.jsxs)("form",{onSubmit:V,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[(0,e.jsx)("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.usersView.editUser}),(0,e.jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,e.jsx)("label",{htmlFor:"edit-user-display-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.displayName}),(0,e.jsx)("input",{id:"edit-user-display-name-input",type:"text",value:s.displayName,onChange:u=>o({...s,displayName:u.target.value}),required:!0,"data-testid":"edit-user-display-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{marginBottom:"1.2rem"},children:[(0,e.jsx)("label",{htmlFor:"edit-user-email-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.email}),(0,e.jsx)("input",{id:"edit-user-email-input",type:"email",value:s.email,onChange:u=>o({...s,email:u.target.value}),required:!0,"data-testid":"edit-user-email",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>o(null),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:t.usersView.cancel}),(0,e.jsx)("button",{type:"submit","data-testid":"submit-edit-user",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",cursor:"pointer"},children:t.usersView.save})]})]})})]})}function P(){let r=(0,h.useLoaderData)(),i=(0,h.useFetcher)(),t=r.labels??w,[l,c]=(0,f.useState)(""),[s,o]=(0,f.useState)(!1),[n,a]=(0,f.useState)(null),[p,x]=(0,f.useState)(""),[d,g]=(0,f.useState)(""),[y,S]=(0,f.useState)(""),C=r.groups.filter(m=>m.name.toLowerCase().includes(l.toLowerCase())||m.groupKey.toLowerCase().includes(l.toLowerCase())),V=m=>{m.preventDefault(),p&&d&&(i.submit({intent:"createGroup",groupKey:p,name:d,description:y},{method:"post"}),x(""),g(""),S(""),o(!1))},u=m=>{m.preventDefault(),n&&(i.submit({intent:"updateGroup",groupId:String(n.id),name:n.name,description:n.description??""},{method:"post"}),a(null))},L=m=>{window.confirm(t.groupsView.confirmDelete)&&i.submit({intent:"deleteGroup",groupId:String(m)},{method:"post"})};return(0,e.jsxs)("section",{"data-testid":"groups-view-section",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[(0,e.jsx)(k,{fetcher:i}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[(0,e.jsx)("input",{type:"text",placeholder:t.groupsView.searchPlaceholder,value:l,onChange:m=>c(m.target.value),"data-testid":"groups-search-input",style:{padding:"6px 12px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",width:"260px"}}),(0,e.jsx)("button",{type:"button",onClick:()=>o(!0),"data-testid":"groups-add-button",style:{padding:"6px 14px",borderRadius:"6px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.groupsView.addGroup})]}),(0,e.jsx)("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(280px, 1fr))",gap:"1rem"},children:C.map(m=>{let B=r.groupMembers.filter(D=>D.groupId===m.id).length;return(0,e.jsxs)("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",padding:"1rem",display:"flex",flexDirection:"column",justifyContent:"space-between"},"data-testid":`group-card-${m.id}`,children:[(0,e.jsxs)("div",{children:[(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"0.5rem"},children:[(0,e.jsx)("h4",{style:{margin:0,fontSize:"1rem",color:"#0f172a"},children:m.name}),(0,e.jsx)("span",{style:{fontSize:"0.72rem",backgroundColor:"#e0e7ff",color:"#4338ca",padding:"2px 6px",borderRadius:"4px",fontFamily:"monospace"},children:m.groupKey})]}),(0,e.jsx)("p",{style:{margin:"0 0 0.8rem 0",fontSize:"0.82rem",color:"#64748b",minHeight:"2.4em"},children:m.description||t.groupsView.noDescription})]}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",borderTop:"1px solid #f1f5f9",paddingTop:"0.6rem"},children:[(0,e.jsxs)("span",{style:{fontSize:"0.78rem",color:"#475569"},children:["\u{1F465} \u30E1\u30F3\u30D0\u30FC: ",(0,e.jsx)("strong",{children:B})," \u4EBA"]}),(0,e.jsxs)("div",{children:[(0,e.jsx)("button",{type:"button",onClick:()=>a(m),"data-testid":`edit-group-btn-${m.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.75rem",cursor:"pointer",marginRight:"4px"},children:t.usersView.edit}),(0,e.jsx)("button",{type:"button",onClick:()=>L(m.id),"data-testid":`delete-group-btn-${m.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #fee2e2",backgroundColor:"#fff5f5",color:"#b91c1c",fontSize:"0.75rem",cursor:"pointer"},children:"\u524A\u9664"})]})]})]},m.id)})}),s&&(0,e.jsx)("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"group-create-modal",children:(0,e.jsxs)("form",{onSubmit:V,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[(0,e.jsx)("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.groupsView.addGroup}),(0,e.jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,e.jsx)("label",{htmlFor:"create-group-key-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.groupKey}),(0,e.jsx)("input",{id:"create-group-key-input",type:"text",value:p,onChange:m=>x(m.target.value),placeholder:t.groupsView.groupKeyPlaceholder,required:!0,"data-testid":"create-group-key",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,e.jsx)("label",{htmlFor:"create-group-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.name}),(0,e.jsx)("input",{id:"create-group-name-input",type:"text",value:d,onChange:m=>g(m.target.value),placeholder:t.groupsView.namePlaceholder,required:!0,"data-testid":"create-group-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{marginBottom:"1.2rem"},children:[(0,e.jsx)("label",{htmlFor:"create-group-desc-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.description}),(0,e.jsx)("textarea",{id:"create-group-desc-input",value:y,onChange:m=>S(m.target.value),"data-testid":"create-group-description",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box",height:"60px"}})]}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>o(!1),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:"\u30AD\u30E3\u30F3\u30BB\u30EB"}),(0,e.jsx)("button",{type:"submit","data-testid":"submit-create-group",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",cursor:"pointer"},children:"\u4F5C\u6210"})]})]})}),n&&(0,e.jsx)("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"group-edit-modal",children:(0,e.jsxs)("form",{onSubmit:u,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[(0,e.jsx)("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.groupsView.editGroup}),(0,e.jsxs)("div",{style:{marginBottom:"1rem"},children:[(0,e.jsx)("label",{htmlFor:"edit-group-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.name}),(0,e.jsx)("input",{id:"edit-group-name-input",type:"text",value:n.name,onChange:m=>a({...n,name:m.target.value}),required:!0,"data-testid":"edit-group-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{marginBottom:"1.2rem"},children:[(0,e.jsx)("label",{htmlFor:"edit-group-desc-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.description}),(0,e.jsx)("textarea",{id:"edit-group-desc-input",value:n.description??"",onChange:m=>a({...n,description:m.target.value}),"data-testid":"edit-group-description",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box",height:"60px"}})]}),(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[(0,e.jsx)("button",{type:"button",onClick:()=>a(null),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:t.groupsView.cancel}),(0,e.jsx)("button",{type:"submit","data-testid":"submit-edit-group",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",cursor:"pointer"},children:t.groupsView.save})]})]})})]})}function E(){let r=(0,h.useLoaderData)(),i=(0,h.useFetcher)(),t=r.labels??w,[l,c]=(0,f.useState)(r.groups[0]?.id??0),s=r.groups.find(a=>a.id===l),o=new Set(r.groupMembers.filter(a=>a.groupId===l).map(a=>a.userId)),n=(a,p)=>{p?i.submit({intent:"removeGroupMember",groupId:String(l),userId:String(a)},{method:"post"}):i.submit({intent:"addGroupMember",groupId:String(l),userId:String(a)},{method:"post"})};return(0,e.jsxs)("section",{"data-testid":"group-members-view-section",style:{display:"grid",gridTemplateColumns:"240px 1fr",gap:"1.2rem"},children:[(0,e.jsx)(k,{fetcher:i}),(0,e.jsxs)("div",{style:{backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[(0,e.jsx)("h4",{style:{margin:"0 0 0.8rem 0",fontSize:"0.9rem",color:"#475569"},children:t.groupMembersView.selectGroup}),(0,e.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"0.4rem"},children:r.groups.map(a=>{let p=a.id===l;return(0,e.jsx)("button",{type:"button",onClick:()=>c(a.id),"data-testid":`select-group-btn-${a.id}`,style:{textAlign:"left",padding:"8px 10px",borderRadius:"6px",border:p?"1px solid #6366f1":"1px solid #e2e8f0",backgroundColor:p?"#e0e7ff":"#ffffff",color:p?"#3730a3":"#0f172a",cursor:"pointer",fontWeight:p?600:400,fontSize:"0.85rem"},children:a.name},a.id)})})]}),(0,e.jsx)("div",{style:{backgroundColor:"#ffffff",padding:"1.2rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:s?(0,e.jsxs)(e.Fragment,{children:[(0,e.jsxs)("div",{style:{marginBottom:"1rem",borderBottom:"1px solid #e2e8f0",paddingBottom:"0.8rem"},children:[(0,e.jsxs)("h3",{style:{margin:0,fontSize:"1.1rem",color:"#0f172a"},children:[s.name," \u306E\u6240\u5C5E\u30E1\u30F3\u30D0\u30FC"]}),(0,e.jsxs)("span",{style:{fontSize:"0.8rem",color:"#64748b"},children:["\u30B0\u30EB\u30FC\u30D7\u30AD\u30FC: ",s.groupKey]})]}),(0,e.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"0.5rem"},children:r.users.map(a=>{let p=o.has(a.id);return(0,e.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"8px 12px",borderRadius:"6px",border:"1px solid #f1f5f9",backgroundColor:p?"#f0fdf4":"#ffffff"},"data-testid":`member-row-${a.id}`,children:[(0,e.jsxs)("div",{children:[(0,e.jsx)("span",{style:{fontWeight:600,fontSize:"0.88rem",color:"#0f172a",marginRight:"8px"},children:a.displayName}),(0,e.jsxs)("span",{style:{fontSize:"0.78rem",color:"#64748b"},children:["(",a.email,")"]})]}),(0,e.jsx)("button",{type:"button",onClick:()=>n(a.id,p),"data-testid":`toggle-member-btn-${a.id}`,style:{padding:"4px 12px",borderRadius:"4px",border:p?"1px solid #b91c1c":"1px solid #0284c7",backgroundColor:p?"#ffffff":"#0284c7",color:p?"#b91c1c":"#ffffff",fontSize:"0.78rem",fontWeight:600,cursor:"pointer"},children:p?t.groupMembersView.actionRemove:t.groupMembersView.actionAdd})]},a.id)})})]}):(0,e.jsx)("div",{style:{color:"#94a3b8",textAlign:"center",padding:"2rem"},children:t.groupMembersView.selectPrompt})})]})}function N(){let r=(0,h.useLoaderData)(),i=(0,h.useFetcher)(),t=r.labels??w,[l,c]=(0,f.useState)(""),[s,o]=(0,f.useState)("email"),[n,a]=(0,f.useState)(""),p=d=>{d.preventDefault(),l&&(i.submit({intent:"addAllowlistEntry",pattern:l,type:s,description:n},{method:"post"}),c(""),a(""))},x=d=>{i.submit({intent:"removeAllowlistEntry",entryId:String(d)},{method:"post"})};return(0,e.jsxs)("section",{"data-testid":"allowlist-view-section",style:{display:"flex",flexDirection:"column",gap:"1.2rem"},children:[(0,e.jsx)(k,{fetcher:i}),(0,e.jsxs)("form",{onSubmit:p,style:{backgroundColor:"#ffffff",padding:"1.2rem",borderRadius:"8px",border:"1px solid #e2e8f0",display:"flex",flexWrap:"wrap",gap:"0.8rem",alignItems:"flex-end"},children:[(0,e.jsxs)("div",{style:{flex:2,minWidth:"220px"},children:[(0,e.jsx)("label",{htmlFor:"allowlist-pattern-input-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.pattern}),(0,e.jsx)("input",{id:"allowlist-pattern-input-field",type:"text",value:l,onChange:d=>c(d.target.value),required:!0,"data-testid":"allowlist-pattern-input",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"}})]}),(0,e.jsxs)("div",{style:{flex:1,minWidth:"140px"},children:[(0,e.jsx)("label",{htmlFor:"allowlist-type-select-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.type}),(0,e.jsxs)("select",{id:"allowlist-type-select-field",value:s,onChange:d=>o(d.target.value),"data-testid":"allowlist-type-select",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"},children:[(0,e.jsx)("option",{value:"email",children:t.allowlistView.emailType}),(0,e.jsx)("option",{value:"domain",children:t.allowlistView.domainType})]})]}),(0,e.jsxs)("div",{style:{flex:2,minWidth:"200px"},children:[(0,e.jsx)("label",{htmlFor:"allowlist-desc-input-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.description}),(0,e.jsx)("input",{id:"allowlist-desc-input-field",type:"text",value:n,onChange:d=>a(d.target.value),"data-testid":"allowlist-desc-input",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"}})]}),(0,e.jsx)("button",{type:"submit","data-testid":"submit-allowlist-add",style:{padding:"7px 16px",borderRadius:"6px",backgroundColor:"#047857",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.allowlistView.addEntry})]}),(0,e.jsx)("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",overflow:"hidden"},children:(0,e.jsxs)("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.85rem",textAlign:"left"},children:[(0,e.jsx)("thead",{children:(0,e.jsxs)("tr",{style:{backgroundColor:"#f8fafc",borderBottom:"1px solid #e2e8f0",color:"#475569"},children:[(0,e.jsx)("th",{style:{padding:"10px 14px"},children:"ID"}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.allowlistView.pattern}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.allowlistView.type}),(0,e.jsx)("th",{style:{padding:"10px 14px"},children:t.allowlistView.description}),(0,e.jsx)("th",{style:{padding:"10px 14px",textAlign:"right"},children:t.allowlistView.actions})]})}),(0,e.jsxs)("tbody",{children:[r.allowlist.map(d=>(0,e.jsxs)("tr",{style:{borderBottom:"1px solid #f1f5f9"},"data-testid":`allowlist-row-${d.id}`,children:[(0,e.jsxs)("td",{style:{padding:"10px 14px",color:"#64748b"},children:["#",d.id]}),(0,e.jsx)("td",{style:{padding:"10px 14px",fontWeight:600,color:"#0f172a",fontFamily:"monospace"},children:d.pattern}),(0,e.jsx)("td",{style:{padding:"10px 14px"},children:(0,e.jsx)("span",{style:{fontSize:"0.75rem",padding:"2px 8px",borderRadius:"10px",backgroundColor:d.type==="email"?"#e0f2fe":"#fef3c7",color:d.type==="email"?"#0369a1":"#b45309",fontWeight:600},children:d.type==="email"?t.allowlistView.emailTypeShort:t.allowlistView.domainTypeShort})}),(0,e.jsx)("td",{style:{padding:"10px 14px",color:"#475569"},children:d.description||"-"}),(0,e.jsx)("td",{style:{padding:"10px 14px",textAlign:"right"},children:d.readOnly?(0,e.jsx)("span",{"data-testid":`readonly-allowlist-${d.id}`,style:{fontSize:"0.75rem",color:"#94a3b8"},children:t.allowlistView.readOnly}):(0,e.jsx)("button",{type:"button",onClick:()=>x(d.id),"data-testid":`remove-allowlist-btn-${d.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #fee2e2",backgroundColor:"#fff5f5",color:"#b91c1c",fontSize:"0.78rem",cursor:"pointer"},children:"\u524A\u9664"})})]},d.id)),r.allowlist.length===0&&(0,e.jsx)("tr",{children:(0,e.jsx)("td",{colSpan:5,style:{padding:"2rem",textAlign:"center",color:"#94a3b8"},children:t.allowlistView.empty})})]})]})})]})}var J={users:M,groups:P,group_members:E,allowlist:N};function X({renderHeader:r}={}){let t=(0,h.useLoaderData)()?.segment??"users",l=J[t]??M;return(0,e.jsx)(z,{renderHeader:r,children:(0,e.jsx)(l,{})})}0&&(module.exports={AllowlistView,AuthUserAdminAppView,GroupMembersView,GroupsView,UserAdminShell,UsersView,createInMemoryUserAdminStore,createUserAdminApp,defaultUserAdminLabels,jaUserAdminLabels,resolveUserAdminLabels});
2
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/admin.ts","../src/admin/labels.ts","../src/admin/server.ts","../src/admin/ui.tsx","../src/admin/views.tsx"],"sourcesContent":["/**\n * @aiquants/auth-react-router/admin — user administration UI and store adapters.\n * ユーザー管理 UI およびストアアダプタのバレル。\n */\nexport * from \"./admin/app\"\n","/**\n * Labels and i18n strings for the user administration UI.\n * ユーザー管理 UI で使用するラベルと i18n 文字列の定義。\n *\n * パッケージ既定は **英語** ([defaultUserAdminLabels])。日本語で使う場合はアプリ側が\n * [jaUserAdminLabels] を明示的に注入する (認可管理 `@aiquants/authz-react-router` と同じ規約)。\n * UI 文字列はすべて本モジュールに集約し、コンポーネント側へ直書きしないこと — 直書きすると\n * 日本語以外の利用者がその箇所を差し替えられなくなる。\n */\n\nexport type UserAdminLabels = {\n title: string\n /** シェル (タブ枠) の見出し。 */\n heading: string\n tabs: {\n users: string\n groups: string\n groupMembers: string\n allowlist: string\n }\n usersView: {\n title: string\n searchPlaceholder: string\n addUser: string\n editUser: string\n id: string\n displayName: string\n email: string\n status: string\n active: string\n inactive: string\n actions: string\n edit: string\n activate: string\n deactivate: string\n empty: string\n save: string\n cancel: string\n }\n groupsView: {\n title: string\n searchPlaceholder: string\n addGroup: string\n editGroup: string\n id: string\n groupKey: string\n name: string\n description: string\n memberCount: string\n status: string\n actions: string\n edit: string\n delete: string\n confirmDelete: string\n noDescription: string\n groupKeyPlaceholder: string\n namePlaceholder: string\n empty: string\n save: string\n cancel: string\n }\n groupMembersView: {\n selectGroup: string\n selectPrompt: string\n actionAdd: string\n actionRemove: string\n }\n allowlistView: {\n title: string\n addEntry: string\n pattern: string\n type: string\n emailType: string\n domainType: string\n /** 一覧のバッジ表示 (短縮形)。 */\n emailTypeShort: string\n domainTypeShort: string\n description: string\n actions: string\n /** 変更不能な供給元 (環境変数など) 由来の行に出す表示。 */\n readOnly: string\n delete: string\n empty: string\n save: string\n cancel: string\n }\n}\n\n/**\n * Package default labels (English).\n * パッケージ既定ラベル (英語)。\n */\nexport const defaultUserAdminLabels: UserAdminLabels = {\n title: \"Users & Groups\",\n heading: \"👤 User administration\",\n tabs: {\n users: \"👤 Users\",\n groups: \"👥 Groups\",\n groupMembers: \"🔗 Members\",\n allowlist: \"✉️ Allowlist\",\n },\n usersView: {\n title: \"Users\",\n searchPlaceholder: \"Search by name or email...\",\n addUser: \"+ Add user\",\n editUser: \"Edit user\",\n id: \"ID\",\n displayName: \"Name\",\n email: \"Email\",\n status: \"Status\",\n active: \"Active\",\n inactive: \"Inactive\",\n actions: \"Actions\",\n edit: \"Edit\",\n activate: \"Activate\",\n deactivate: \"Deactivate\",\n empty: \"No matching users\",\n save: \"Save\",\n cancel: \"Cancel\",\n },\n groupsView: {\n title: \"Groups\",\n searchPlaceholder: \"Search by group name or key...\",\n addGroup: \"+ Add group\",\n editGroup: \"Edit group\",\n id: \"ID\",\n groupKey: \"Group key\",\n name: \"Name\",\n description: \"Description\",\n memberCount: \"Members\",\n status: \"Status\",\n actions: \"Actions\",\n edit: \"Edit\",\n delete: \"Delete\",\n confirmDelete: \"Delete this group?\",\n noDescription: \"No description\",\n groupKeyPlaceholder: \"e.g. dev-team\",\n namePlaceholder: \"e.g. Development team\",\n empty: \"No groups registered\",\n save: \"Save\",\n cancel: \"Cancel\",\n },\n groupMembersView: {\n selectGroup: \"Select a group\",\n selectPrompt: \"Select a group to manage its members\",\n actionAdd: \"Add\",\n actionRemove: \"Remove\",\n },\n allowlistView: {\n title: \"Sign-in allowlist\",\n addEntry: \"+ Add pattern\",\n pattern: \"Pattern (e.g. @example.com or user@example.com)\",\n type: \"Type\",\n emailType: \"Exact email match\",\n domainType: \"Domain match\",\n emailTypeShort: \"Email\",\n domainTypeShort: \"Domain\",\n description: \"Description\",\n actions: \"Actions\",\n readOnly: \"Read-only\",\n delete: \"Delete\",\n empty: \"No allowlist entries registered\",\n save: \"Add\",\n cancel: \"Cancel\",\n },\n}\n\n/**\n * Standard Japanese labels for user administration (opt-in).\n * ユーザー管理の標準日本語ラベル (アプリ側で明示注入して使う)。\n */\nexport const jaUserAdminLabels: UserAdminLabels = {\n title: \"ユーザー・グループ管理\",\n heading: \"👤 ユーザー管理\",\n tabs: {\n users: \"👤 ユーザー一覧\",\n groups: \"👥 ユーザーグループ\",\n groupMembers: \"🔗 グループメンバー\",\n allowlist: \"✉️ アクセス許可リスト\",\n },\n usersView: {\n title: \"ユーザー管理\",\n searchPlaceholder: \"表示名や Email で検索...\",\n addUser: \"+ ユーザー追加\",\n editUser: \"ユーザーの編集\",\n id: \"ID\",\n displayName: \"表示名\",\n email: \"メールアドレス\",\n status: \"ステータス\",\n active: \"有効\",\n inactive: \"無効\",\n actions: \"操作\",\n edit: \"編集\",\n activate: \"有効化\",\n deactivate: \"無効化\",\n empty: \"該当するユーザーが見つかりません\",\n save: \"保存\",\n cancel: \"キャンセル\",\n },\n groupsView: {\n title: \"ユーザーグループ管理\",\n searchPlaceholder: \"グループ名やキーで検索...\",\n addGroup: \"+ グループ作成\",\n editGroup: \"グループの編集\",\n id: \"ID\",\n groupKey: \"グループキー\",\n name: \"グループ名\",\n description: \"説明\",\n memberCount: \"メンバー数\",\n status: \"ステータス\",\n actions: \"操作\",\n edit: \"編集\",\n delete: \"削除\",\n confirmDelete: \"このグループを削除してもよろしいですか?\",\n noDescription: \"説明なし\",\n groupKeyPlaceholder: \"例: dev-team\",\n namePlaceholder: \"例: 開発チーム\",\n empty: \"グループが登録されていません\",\n save: \"保存\",\n cancel: \"キャンセル\",\n },\n groupMembersView: {\n selectGroup: \"グループ選択\",\n selectPrompt: \"グループを選択してください\",\n actionAdd: \"追加\",\n actionRemove: \"除外\",\n },\n allowlistView: {\n title: \"アクセス許可リスト (Allowlist)\",\n addEntry: \"+ 許可パターン追加\",\n pattern: \"パターン (例: @example.com または user@example.com)\",\n type: \"種別\",\n emailType: \"メールアドレス完全一致\",\n domainType: \"ドメイン一致\",\n emailTypeShort: \"Email 完全一致\",\n domainTypeShort: \"ドメイン一致\",\n description: \"説明 / 備考\",\n actions: \"操作\",\n readOnly: \"変更不可\",\n delete: \"削除\",\n empty: \"アロワリストエントリが登録されていません\",\n save: \"登録\",\n cancel: \"キャンセル\",\n },\n}\n\n/** セクション単位の部分上書き。 */\nexport type PartialUserAdminLabels = {\n title?: string\n heading?: string\n tabs?: Partial<UserAdminLabels[\"tabs\"]>\n usersView?: Partial<UserAdminLabels[\"usersView\"]>\n groupsView?: Partial<UserAdminLabels[\"groupsView\"]>\n groupMembersView?: Partial<UserAdminLabels[\"groupMembersView\"]>\n allowlistView?: Partial<UserAdminLabels[\"allowlistView\"]>\n}\n\n/**\n * Shallow per-section merge over the English defaults.\n * 英語既定に対してセクション単位で浅くマージする処理。\n */\nexport function resolveUserAdminLabels(over?: PartialUserAdminLabels): UserAdminLabels {\n const d = defaultUserAdminLabels\n if (!over) return d\n return {\n title: over.title ?? d.title,\n heading: over.heading ?? d.heading,\n tabs: { ...d.tabs, ...over.tabs },\n usersView: { ...d.usersView, ...over.usersView },\n groupsView: { ...d.groupsView, ...over.groupsView },\n groupMembersView: { ...d.groupMembersView, ...over.groupMembersView },\n allowlistView: { ...d.allowlistView, ...over.allowlistView },\n }\n}\n","import type { AuthAllowlistEntry, AuthGroup, AuthGroupMember, AuthUser } from \"@aiquants/auth-core\"\nimport type { ActionFunctionArgs, LoaderFunctionArgs } from \"react-router\"\nimport { type PartialUserAdminLabels, resolveUserAdminLabels } from \"./labels\"\n\n/**\n * Server store interface for managing users, groups, memberships, and allowlists.\n * ユーザー・グループ・所属・アロワリストを管理するサーバー層ストアのインターフェース。\n */\nexport type UserAdminStore = {\n listUsers: () => Promise<AuthUser[]>\n createUser: (data: { displayName: string; email: string }) => Promise<AuthUser>\n updateUser: (id: number, data: { displayName: string; email: string }) => Promise<AuthUser>\n toggleUserActive: (id: number, isActive: boolean) => Promise<AuthUser>\n\n listGroups: () => Promise<AuthGroup[]>\n createGroup: (data: { groupKey: string; name: string; description?: string }) => Promise<AuthGroup>\n updateGroup: (id: number, data: { name: string; description?: string }) => Promise<AuthGroup>\n deleteGroup: (id: number) => Promise<void>\n\n listGroupMembers: () => Promise<AuthGroupMember[]>\n addGroupMember: (groupId: number, userId: number) => Promise<AuthGroupMember>\n removeGroupMember: (groupId: number, userId: number) => Promise<void>\n\n listAllowlist: () => Promise<AuthAllowlistEntry[]>\n addAllowlistEntry: (data: { pattern: string; type: \"email\" | \"domain\"; description?: string }) => Promise<AuthAllowlistEntry>\n removeAllowlistEntry: (id: number) => Promise<void>\n}\n\n/**\n * Creates an in-memory UserAdminStore initialized with optional seed data.\n * シードデータで初期化されるメモリ上の UserAdminStore を作成する。\n */\nexport function createInMemoryUserAdminStore(initial?: { users?: AuthUser[]; groups?: AuthGroup[]; groupMembers?: AuthGroupMember[]; allowlist?: AuthAllowlistEntry[] }): UserAdminStore {\n const users: AuthUser[] = initial?.users ?? [\n { id: 1, email: \"admin@example.com\", displayName: \"システム管理者\", isActive: true },\n { id: 2, email: \"user1@example.com\", displayName: \"開発 太郎\", isActive: true },\n { id: 3, email: \"user2@example.com\", displayName: \"営業 花子\", isActive: true },\n ]\n let groups: AuthGroup[] = initial?.groups ?? [\n { id: 1, groupKey: \"dev-team\", name: \"開発チーム\", description: \"システム開発担当グループ\", isActive: true },\n { id: 2, groupKey: \"sales-dept\", name: \"営業部\", description: \"営業ソリューション担当\", isActive: true },\n ]\n let groupMembers: AuthGroupMember[] = initial?.groupMembers ?? [\n { id: 1, groupId: 1, userId: 2 },\n { id: 2, groupId: 2, userId: 3 },\n ]\n let allowlist: AuthAllowlistEntry[] = initial?.allowlist ?? [\n { id: 1, pattern: \"admin@example.com\", type: \"email\", description: \"初期管理者\" },\n { id: 2, pattern: \"@example.com\", type: \"domain\", description: \"社内ドメイン\" },\n ]\n\n return {\n async listUsers() {\n return [...users]\n },\n async createUser(data) {\n const newUser: AuthUser = {\n id: Math.max(...users.map((u) => u.id), 0) + 1,\n email: data.email,\n displayName: data.displayName,\n isActive: true,\n }\n users.push(newUser)\n return newUser\n },\n async updateUser(id, data) {\n const idx = users.findIndex((u) => u.id === id)\n if (idx === -1) throw new Error(`User not found: ${id}`)\n users[idx] = { ...users[idx], displayName: data.displayName, email: data.email }\n return users[idx]\n },\n async toggleUserActive(id, isActive) {\n const idx = users.findIndex((u) => u.id === id)\n if (idx === -1) throw new Error(`User not found: ${id}`)\n users[idx] = { ...users[idx], isActive }\n return users[idx]\n },\n\n async listGroups() {\n return [...groups]\n },\n async createGroup(data) {\n const newGroup: AuthGroup = {\n id: Math.max(...groups.map((g) => g.id), 0) + 1,\n groupKey: data.groupKey,\n name: data.name,\n description: data.description,\n isActive: true,\n }\n groups.push(newGroup)\n return newGroup\n },\n async updateGroup(id, data) {\n const idx = groups.findIndex((g) => g.id === id)\n if (idx === -1) throw new Error(`Group not found: ${id}`)\n groups[idx] = { ...groups[idx], name: data.name, description: data.description }\n return groups[idx]\n },\n async deleteGroup(id) {\n groups = groups.filter((g) => g.id !== id)\n groupMembers = groupMembers.filter((m) => m.groupId !== id)\n },\n\n async listGroupMembers() {\n return [...groupMembers]\n },\n async addGroupMember(groupId, userId) {\n const existing = groupMembers.find((m) => m.groupId === groupId && m.userId === userId)\n if (existing) return existing\n const newMember: AuthGroupMember = {\n id: Math.max(...groupMembers.map((m) => m.id), 0) + 1,\n groupId,\n userId,\n }\n groupMembers.push(newMember)\n return newMember\n },\n async removeGroupMember(groupId, userId) {\n groupMembers = groupMembers.filter((m) => !(m.groupId === groupId && m.userId === userId))\n },\n\n async listAllowlist() {\n return [...allowlist]\n },\n async addAllowlistEntry(data) {\n const newEntry: AuthAllowlistEntry = {\n id: Math.max(...allowlist.map((a) => a.id), 0) + 1,\n pattern: data.pattern,\n type: data.type,\n description: data.description,\n }\n allowlist.push(newEntry)\n return newEntry\n },\n async removeAllowlistEntry(id) {\n allowlist = allowlist.filter((a) => a.id !== id)\n },\n }\n}\n\n/**\n * The CRUD verb an operation performs. Deliberately a plain string union so this package stays\n * independent of any particular authorization library.\n * 操作が行う CRUD 種別。特定の認可ライブラリに依存しないよう素の文字列 union で表現する。\n */\nexport type UserAdminAccess = \"read\" | \"create\" | \"update\" | \"delete\"\n\n/**\n * Access guard the user-admin surface is mounted behind. Deny by throwing\n * (a `Response`/`redirect` or an `Error`); returning normally means \"allow\".\n *\n * ユーザー管理面をマウントする際の必須アクセスガード。拒否は throw で表現する\n * (`Response` / `redirect` / `Error`)。正常 return は「許可」を意味する。\n *\n * 操作ごとの CRUD 種別を受け取るため、呼び出し側は「削除だけ別権限」といった細分化ができる。\n * `intent` は監査ログ用の補助情報(認可判断は `action` で行うこと)。\n */\nexport type UserAdminGuards = {\n /**\n * 許可は「正常 return」、拒否は「throw」で表す。返り値は判定に使わない (`void` 契約)。\n * 述語のつもりで `false` を返す実装を書くと素通りするため、返り値型で明示的に禁じる。\n */\n requireAccess: (request: Request, context: { action: UserAdminAccess; intent: string }) => Promise<void> | void\n}\n\n/**\n * Maps each action intent to the CRUD verb it actually performs.\n * 各 intent が実際に行う操作の CRUD 種別への対応表。\n *\n * 割当の付与・解除は行の追加・削除であるため、認可管理 UI (`/authz`) の\n * `assignUserRole` = create / `removeUserRole` = delete と同じ粒度に揃える。\n */\nconst INTENT_ACCESS = new Map<string, UserAdminAccess>([\n [\"createUser\", \"create\"],\n [\"updateUser\", \"update\"],\n [\"toggleUserActive\", \"update\"],\n [\"createGroup\", \"create\"],\n [\"updateGroup\", \"update\"],\n [\"deleteGroup\", \"delete\"],\n [\"addGroupMember\", \"create\"],\n [\"removeGroupMember\", \"delete\"],\n [\"addAllowlistEntry\", \"create\"],\n [\"removeAllowlistEntry\", \"delete\"],\n])\n\nexport type UserAdminAppConfig = {\n store: UserAdminStore\n /** セクション単位の部分上書き。既定は英語 (`defaultUserAdminLabels`)。 */\n labels?: PartialUserAdminLabels\n /**\n * Required — omitting a guard would expose the whole user directory and its write actions.\n * 必須。省略を許すとユーザー名簿と全更新操作が無防備に露出するため、型で強制する。\n */\n guards: UserAdminGuards\n /** ヘッダー注釈に自分の実効権限を表示するためのリクエスト別権限取得関数。 */\n getMyPermissions?: (request: Request) => Promise<Record<string, unknown>>\n}\n\n/**\n * Reads a required positive integer id from submitted form data.\n * 送信フォームから必須の正整数 ID を読み取る処理。\n *\n * @throws when the field is missing, non-numeric, or not a positive integer.\n * 欠落・非数値・正整数でない場合は throw。\n */\nfunction requireId(formData: FormData, field: string): number {\n const raw = formData.get(field)\n const n = Number(raw)\n // NaN / 小数 / 0 以下は不正な ID として弾く (Number(null) === 0 の取り違えも防ぐ)\n if (raw === null || String(raw).trim() === \"\" || !Number.isInteger(n) || n <= 0) {\n throw new Error(`Invalid or missing \"${field}\"`)\n }\n return n\n}\n\n/**\n * Reads a required non-empty trimmed string from submitted form data.\n * 送信フォームから必須の非空文字列 (トリム済み) を読み取る処理。\n *\n * @throws when the field is missing or blank. 欠落・空白のみの場合は throw。\n */\nfunction requireText(formData: FormData, field: string): string {\n const value = String(formData.get(field) ?? \"\").trim()\n if (value === \"\") throw new Error(`Invalid or missing \"${field}\"`)\n return value\n}\n\n/** Reads an optional trimmed string; blank becomes `undefined`. 任意文字列の読み取り (空は undefined)。 */\nfunction optionalText(formData: FormData, field: string): string | undefined {\n const value = String(formData.get(field) ?? \"\").trim()\n return value === \"\" ? undefined : value\n}\n\n/**\n * Creates React Router loader & action handler for user administration.\n * ユーザー管理用 React Router の loader および action ハンドラーを作成する。\n *\n * `config.guards` は必須。loader / action の双方が、処理を始める前にガードを通過させる。\n */\nexport function createUserAdminApp(config: UserAdminAppConfig) {\n const labels = resolveUserAdminLabels(config.labels)\n\n async function loader({ request }: LoaderFunctionArgs) {\n // 読取ガード: 拒否は throw されるためここで処理が打ち切られる\n await config.guards.requireAccess(request, { action: \"read\", intent: \"loader\" })\n const url = new URL(request.url)\n const pathSegments = url.pathname.split(\"/\").filter(Boolean)\n const segment = pathSegments[pathSegments.length - 1] ?? \"users\"\n\n const [users, groups, groupMembers, allowlist, myPermissions] = await Promise.all([\n config.store.listUsers(),\n config.store.listGroups(),\n config.store.listGroupMembers(),\n config.store.listAllowlist(),\n config.getMyPermissions ? config.getMyPermissions(request).catch(() => ({})) : Promise.resolve({}),\n ])\n\n return {\n segment: [\"groups\", \"group_members\", \"allowlist\"].includes(segment) ? segment : \"users\",\n users,\n groups,\n groupMembers,\n allowlist,\n myPermissions,\n labels,\n }\n }\n\n async function action({ request }: ActionFunctionArgs) {\n // intent の判定に FormData が要るため、ここでだけ本文を先に読む。\n // 未知 intent は「何も実行しない」ため、ガード前に読んでも権限判断には影響しない。\n const formData = await request.formData()\n const intent = String(formData.get(\"intent\") ?? \"\")\n\n // 未知 intent はストアへ到達しない (ガードも走らせず 400 相当を返す)。\n // Map 参照のためプロトタイプ継承キー (\"constructor\" 等) を拾わない。\n const access = INTENT_ACCESS.get(intent)\n if (!access) return { ok: false, error: `Unknown intent: ${intent}` }\n\n // 操作ごとの CRUD 種別でガードする (削除は delete 権限が要る)\n await config.guards.requireAccess(request, { action: access, intent })\n\n try {\n return await dispatch(intent, formData)\n } catch (error) {\n // ガード拒否 (Response/redirect) はそのまま伝播させる。ストア由来の業務エラーだけ\n // {ok:false,error} に写して UI へ届ける (ロックアウト理由・重複パターン等)。\n if (error instanceof Response) throw error\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n\n /** intent ごとの実処理。認可・入力検証を通過した後にのみ呼ばれる。 */\n async function dispatch(intent: string, formData: FormData) {\n if (intent === \"createUser\") {\n await config.store.createUser({ displayName: requireText(formData, \"displayName\"), email: requireText(formData, \"email\") })\n return { ok: true }\n }\n\n if (intent === \"updateUser\") {\n await config.store.updateUser(requireId(formData, \"userId\"), { displayName: requireText(formData, \"displayName\"), email: requireText(formData, \"email\") })\n return { ok: true }\n }\n\n if (intent === \"toggleUserActive\") {\n // 破壊側 (無効化) を既定にしない。明示的な \"true\"/\"false\" 以外は不正入力として弾く。\n const rawActive = String(formData.get(\"isActive\") ?? \"\")\n if (rawActive !== \"true\" && rawActive !== \"false\") throw new Error('Invalid or missing \"isActive\"')\n await config.store.toggleUserActive(requireId(formData, \"userId\"), rawActive === \"true\")\n return { ok: true }\n }\n\n if (intent === \"createGroup\") {\n await config.store.createGroup({ groupKey: requireText(formData, \"groupKey\"), name: requireText(formData, \"name\"), description: optionalText(formData, \"description\") })\n return { ok: true }\n }\n\n if (intent === \"updateGroup\") {\n await config.store.updateGroup(requireId(formData, \"groupId\"), { name: requireText(formData, \"name\"), description: optionalText(formData, \"description\") })\n return { ok: true }\n }\n\n if (intent === \"deleteGroup\") {\n await config.store.deleteGroup(requireId(formData, \"groupId\"))\n return { ok: true }\n }\n\n if (intent === \"addGroupMember\") {\n await config.store.addGroupMember(requireId(formData, \"groupId\"), requireId(formData, \"userId\"))\n return { ok: true }\n }\n\n if (intent === \"removeGroupMember\") {\n await config.store.removeGroupMember(requireId(formData, \"groupId\"), requireId(formData, \"userId\"))\n return { ok: true }\n }\n\n if (intent === \"addAllowlistEntry\") {\n const type: \"email\" | \"domain\" = formData.get(\"type\") === \"domain\" ? \"domain\" : \"email\"\n await config.store.addAllowlistEntry({ pattern: requireText(formData, \"pattern\"), type, description: optionalText(formData, \"description\") })\n return { ok: true }\n }\n\n if (intent === \"removeAllowlistEntry\") {\n await config.store.removeAllowlistEntry(requireId(formData, \"entryId\"))\n return { ok: true }\n }\n\n // INTENT_ACCESS を通過した intent は上のいずれかで処理済み (到達しない安全網)\n return { ok: false, error: `Unknown intent: ${intent}` }\n }\n\n return { loader, action }\n}\n","import type React from \"react\"\nimport { Link, useLoaderData } from \"react-router\"\nimport { defaultUserAdminLabels, type UserAdminLabels } from \"./labels\"\n\nexport type UserAdminShellProps = {\n children?: React.ReactNode\n renderHeader?: (props: { title: string; annotation: React.ReactNode }) => React.ReactNode\n}\n\n/**\n * Shell layout for user administration pages with header annotation delegation.\n * ヘッダー注釈委譲を備えたユーザー管理ページのシェルレイアウト。\n */\nfunction GroupPill({ label, summary }: { label: string; summary: { text: string; color: string; bgColor: string; borderColor: string } }) {\n return (\n <span\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"0.18rem\",\n fontSize: \"0.65rem\",\n padding: \"0.08rem 0.35rem\",\n borderRadius: \"9999px\",\n backgroundColor: summary.bgColor,\n color: summary.color,\n border: `1px solid ${summary.borderColor}`,\n fontWeight: 600,\n lineHeight: \"1.2\",\n }}>\n <span>{label}:</span>\n <span>{summary.text}</span>\n </span>\n )\n}\n\nexport function UserAdminShell({ children, renderHeader }: UserAdminShellProps) {\n const data = useLoaderData() as { segment?: string; labels?: UserAdminLabels; myPermissions?: Record<string, unknown> } | undefined\n const segment = data?.segment ?? \"users\"\n const labels = data?.labels ?? defaultUserAdminLabels\n const myPermissions = data?.myPermissions ?? {}\n\n const tabs = [\n { key: \"users\", label: labels.tabs.users, href: \"/users\" },\n { key: \"groups\", label: labels.tabs.groups, href: \"/users/groups\" },\n { key: \"group_members\", label: labels.tabs.groupMembers, href: \"/users/group_members\" },\n { key: \"allowlist\", label: labels.tabs.allowlist, href: \"/users/allowlist\" },\n ]\n\n const permEntries = Object.entries(myPermissions)\n const displayEntries = permEntries.length > 0 ? permEntries : [[\"user_admin\", { read: true, write: true, delete: true, allowedActions: [\"read\", \"create\", \"update\", \"delete\"] }]]\n\n const getSummary = (perm: unknown) => {\n const p = perm as { read?: boolean; write?: boolean; delete?: boolean; allowedActions?: string[] } | undefined\n const allowed = p?.allowedActions ?? []\n const canWrite = p?.write ?? (allowed.includes(\"create\") || allowed.includes(\"update\") || allowed.includes(\"delete\"))\n const canRead = p?.read ?? allowed.includes(\"read\")\n\n if (canWrite) {\n return { text: \"編集可\", color: \"#047857\", bgColor: \"#ecfdf5\", borderColor: \"#a7f3d0\" }\n }\n if (canRead) {\n return { text: \"閲覧のみ\", color: \"#1d4ed8\", bgColor: \"#eff6ff\", borderColor: \"#bfdbfe\" }\n }\n return { text: \"権限なし\", color: \"#64748b\", bgColor: \"#f1f5f9\", borderColor: \"#cbd5e1\" }\n }\n\n const annotation = (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.4rem\" }} data-testid=\"user-admin-header-annotation\">\n <span style={{ fontSize: \"0.75rem\", backgroundColor: \"#e0f2fe\", color: \"#0369a1\", padding: \"2px 8px\", borderRadius: \"12px\", fontWeight: 600 }}>{labels.heading}</span>\n\n <div style={{ display: \"inline-flex\", alignItems: \"center\", gap: \"0.25rem\", fontSize: \"0.68rem\", color: \"#475569\" }} data-testid=\"user-admin-permissions-badge\">\n <span style={{ fontWeight: 600, color: \"#334155\" }}>あなたの権限:</span>\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: \"0.18rem\" }}>\n {displayEntries.map(([resKey, perm], idx) => (\n <GroupPill key={String(resKey || idx)} label={resKey === \"user_admin\" ? \"ユーザー管理UI\" : String(resKey)} summary={getSummary(perm)} />\n ))}\n </span>\n </div>\n </div>\n )\n\n return (\n <div data-testid=\"user-admin-shell\" style={{ width: \"100%\", minHeight: \"100vh\", backgroundColor: \"#f8fafc\", padding: \"1.5rem\", boxSizing: \"border-box\" }}>\n {renderHeader ? (\n renderHeader({ title: labels.title, annotation })\n ) : (\n <div style={{ marginBottom: \"1.5rem\", display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\" }}>\n <h1 style={{ margin: 0, fontSize: \"1.5rem\", fontWeight: 700, color: \"#0f172a\" }}>{labels.title}</h1>\n {annotation}\n </div>\n )}\n\n <div style={{ display: \"flex\", gap: \"0.5rem\", marginBottom: \"1.2rem\", borderBottom: \"2px solid #e2e8f0\", paddingBottom: \"0.5rem\" }}>\n {tabs.map((tab) => {\n const isActive = segment === tab.key\n return (\n <Link\n key={tab.key}\n to={tab.href}\n data-testid={`user-admin-tab-${tab.key}`}\n style={{\n padding: \"0.5rem 1rem\",\n borderRadius: \"6px 6px 0 0\",\n fontSize: \"0.88rem\",\n fontWeight: isActive ? 700 : 500,\n color: isActive ? \"#0284c7\" : \"#64748b\",\n backgroundColor: isActive ? \"#ffffff\" : \"transparent\",\n textDecoration: \"none\",\n borderBottom: isActive ? \"3px solid #0284c7\" : \"3px solid transparent\",\n transition: \"all 0.15s ease\",\n }}>\n {tab.label}\n </Link>\n )\n })}\n </div>\n\n <main>{children}</main>\n </div>\n )\n}\n","import type { AuthAllowlistEntry, AuthGroup, AuthGroupMember, AuthUser } from \"@aiquants/auth-core\"\nimport type React from \"react\"\nimport { useState } from \"react\"\nimport { useFetcher, useLoaderData } from \"react-router\"\nimport { defaultUserAdminLabels, type UserAdminLabels } from \"./labels\"\nimport { UserAdminShell, type UserAdminShellProps } from \"./ui\"\n\nexport type UserAdminLoaderData = {\n segment: string\n users: AuthUser[]\n groups: AuthGroup[]\n groupMembers: AuthGroupMember[]\n allowlist: AuthAllowlistEntry[]\n labels?: UserAdminLabels\n}\n\n/**\n * View for managing users (list, search, toggle active, modal edit).\n * ユーザー管理ビュー (一覧・検索・アクティブ切替・ダイアログ編集)。\n */\n/**\n * Renders the error returned by the last action submission, if any.\n * 直近の action 送信が返したエラーを表示する部品。\n *\n * ロックアウト理由・重複パターン・入力不正などの文言は action が `{ok:false,error}` で返す。\n * 表示しなければ操作が黙って失敗したように見えるため、各ビューの先頭に必ず置く。\n */\nfunction ActionError({ fetcher }: { fetcher: { data?: unknown } }) {\n const result = fetcher.data as { ok?: boolean; error?: string } | undefined\n if (result?.ok !== false || !result.error) return null\n return (\n <div data-testid=\"user-admin-action-error\" role=\"alert\" style={{ padding: \"0.7rem 1rem\", borderRadius: \"6px\", border: \"1px solid #fecaca\", backgroundColor: \"#fef2f2\", color: \"#b91c1c\", fontSize: \"0.85rem\" }}>\n {result.error}\n </div>\n )\n}\n\nexport function UsersView() {\n const data = useLoaderData() as UserAdminLoaderData\n const fetcher = useFetcher()\n const labels = data.labels ?? defaultUserAdminLabels\n\n const [searchQuery, setSearchQuery] = useState(\"\")\n const [editingUser, setEditingUser] = useState<AuthUser | null>(null)\n const [isCreateOpen, setIsCreateOpen] = useState(false)\n\n const [newDisplayName, setNewDisplayName] = useState(\"\")\n const [newEmail, setNewEmail] = useState(\"\")\n\n const filteredUsers = data.users.filter((u) => u.displayName.toLowerCase().includes(searchQuery.toLowerCase()) || u.email.toLowerCase().includes(searchQuery.toLowerCase()))\n\n const handleToggleActive = (user: AuthUser) => {\n fetcher.submit({ intent: \"toggleUserActive\", userId: String(user.id), isActive: String(!user.isActive) }, { method: \"post\" })\n }\n\n const handleCreateUser = (e: React.FormEvent) => {\n e.preventDefault()\n if (!(newDisplayName && newEmail)) return\n fetcher.submit({ intent: \"createUser\", displayName: newDisplayName, email: newEmail }, { method: \"post\" })\n setNewDisplayName(\"\")\n setNewEmail(\"\")\n setIsCreateOpen(false)\n }\n\n const handleUpdateUser = (e: React.FormEvent) => {\n e.preventDefault()\n if (!editingUser) return\n fetcher.submit({ intent: \"updateUser\", userId: String(editingUser.id), displayName: editingUser.displayName, email: editingUser.email }, { method: \"post\" })\n setEditingUser(null)\n }\n\n return (\n <section data-testid=\"users-view-section\" style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}>\n <ActionError fetcher={fetcher} />\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", backgroundColor: \"#ffffff\", padding: \"1rem\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\" }}>\n <input\n type=\"text\"\n placeholder={labels.usersView.searchPlaceholder}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n data-testid=\"users-search-input\"\n style={{ padding: \"6px 12px\", borderRadius: \"6px\", border: \"1px solid #cbd5e1\", fontSize: \"0.85rem\", width: \"260px\" }}\n />\n <button type=\"button\" onClick={() => setIsCreateOpen(true)} data-testid=\"users-add-button\" style={{ padding: \"6px 14px\", borderRadius: \"6px\", backgroundColor: \"#0284c7\", color: \"#ffffff\", border: \"none\", fontSize: \"0.85rem\", fontWeight: 600, cursor: \"pointer\" }}>\n {labels.usersView.addUser}\n </button>\n </div>\n\n <div style={{ backgroundColor: \"#ffffff\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\", overflow: \"hidden\" }}>\n <table style={{ width: \"100%\", borderCollapse: \"collapse\", fontSize: \"0.85rem\", textAlign: \"left\" }}>\n <thead>\n <tr style={{ backgroundColor: \"#f8fafc\", borderBottom: \"1px solid #e2e8f0\", color: \"#475569\" }}>\n <th style={{ padding: \"10px 14px\" }}>{labels.usersView.id}</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.usersView.displayName}</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.usersView.email}</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.usersView.status}</th>\n <th style={{ padding: \"10px 14px\", textAlign: \"right\" }}>{labels.usersView.actions}</th>\n </tr>\n </thead>\n <tbody>\n {filteredUsers.map((u) => (\n <tr key={u.id} style={{ borderBottom: \"1px solid #f1f5f9\" }} data-testid={`user-row-${u.id}`}>\n <td style={{ padding: \"10px 14px\", color: \"#64748b\" }}>#{u.id}</td>\n <td style={{ padding: \"10px 14px\", fontWeight: 600, color: \"#0f172a\" }}>{u.displayName}</td>\n <td style={{ padding: \"10px 14px\", color: \"#475569\" }}>{u.email}</td>\n <td style={{ padding: \"10px 14px\" }}>\n <span style={{ fontSize: \"0.75rem\", padding: \"2px 8px\", borderRadius: \"10px\", backgroundColor: u.isActive ? \"#dcfce7\" : \"#fee2e2\", color: u.isActive ? \"#15803d\" : \"#b91c1c\", fontWeight: 600 }}>\n {u.isActive ? labels.usersView.active : labels.usersView.inactive}\n </span>\n </td>\n <td style={{ padding: \"10px 14px\", textAlign: \"right\" }}>\n <button\n type=\"button\"\n onClick={() => setEditingUser(u)}\n data-testid={`edit-user-btn-${u.id}`}\n style={{ padding: \"3px 8px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", fontSize: \"0.78rem\", cursor: \"pointer\", marginRight: \"6px\" }}>\n {labels.usersView.edit}\n </button>\n <button\n type=\"button\"\n onClick={() => handleToggleActive(u)}\n data-testid={`toggle-user-btn-${u.id}`}\n style={{ padding: \"3px 8px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", fontSize: \"0.78rem\", cursor: \"pointer\", color: u.isActive ? \"#b91c1c\" : \"#15803d\" }}>\n {u.isActive ? labels.usersView.deactivate : labels.usersView.activate}\n </button>\n </td>\n </tr>\n ))}\n {filteredUsers.length === 0 && (\n <tr>\n <td colSpan={5} style={{ padding: \"2rem\", textAlign: \"center\", color: \"#94a3b8\" }}>\n {labels.usersView.empty}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n\n {/* Create Modal */}\n {isCreateOpen && (\n <div style={{ position: \"fixed\", inset: 0, backgroundColor: \"rgba(0,0,0,0.4)\", display: \"flex\", justifyContent: \"center\", alignItems: \"center\", zIndex: 100 }} data-testid=\"user-create-modal\">\n <form onSubmit={handleCreateUser} style={{ backgroundColor: \"#ffffff\", padding: \"1.5rem\", borderRadius: \"8px\", width: \"360px\", boxShadow: \"0 10px 25px rgba(0,0,0,0.15)\" }}>\n <h3 style={{ margin: \"0 0 1rem 0\", fontSize: \"1.1rem\" }}>{labels.usersView.addUser}</h3>\n <div style={{ marginBottom: \"1rem\" }}>\n <label htmlFor=\"create-user-display-name-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.usersView.displayName}\n </label>\n <input\n id=\"create-user-display-name-input\"\n type=\"text\"\n value={newDisplayName}\n onChange={(e) => setNewDisplayName(e.target.value)}\n required\n data-testid=\"create-user-display-name\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ marginBottom: \"1.2rem\" }}>\n <label htmlFor=\"create-user-email-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.usersView.email}\n </label>\n <input\n id=\"create-user-email-input\"\n type=\"email\"\n value={newEmail}\n onChange={(e) => setNewEmail(e.target.value)}\n required\n data-testid=\"create-user-email\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"0.5rem\" }}>\n <button type=\"button\" onClick={() => setIsCreateOpen(false)} style={{ padding: \"6px 12px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", cursor: \"pointer\" }}>\n キャンセル\n </button>\n <button type=\"submit\" data-testid=\"submit-create-user\" style={{ padding: \"6px 12px\", borderRadius: \"4px\", backgroundColor: \"#0284c7\", color: \"#ffffff\", border: \"none\", cursor: \"pointer\" }}>\n 作成\n </button>\n </div>\n </form>\n </div>\n )}\n\n {/* Edit Modal */}\n {editingUser && (\n <div style={{ position: \"fixed\", inset: 0, backgroundColor: \"rgba(0,0,0,0.4)\", display: \"flex\", justifyContent: \"center\", alignItems: \"center\", zIndex: 100 }} data-testid=\"user-edit-modal\">\n <form onSubmit={handleUpdateUser} style={{ backgroundColor: \"#ffffff\", padding: \"1.5rem\", borderRadius: \"8px\", width: \"360px\", boxShadow: \"0 10px 25px rgba(0,0,0,0.15)\" }}>\n <h3 style={{ margin: \"0 0 1rem 0\", fontSize: \"1.1rem\" }}>{labels.usersView.editUser}</h3>\n <div style={{ marginBottom: \"1rem\" }}>\n <label htmlFor=\"edit-user-display-name-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.usersView.displayName}\n </label>\n <input\n id=\"edit-user-display-name-input\"\n type=\"text\"\n value={editingUser.displayName}\n onChange={(e) => setEditingUser({ ...editingUser, displayName: e.target.value })}\n required\n data-testid=\"edit-user-display-name\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ marginBottom: \"1.2rem\" }}>\n <label htmlFor=\"edit-user-email-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.usersView.email}\n </label>\n <input\n id=\"edit-user-email-input\"\n type=\"email\"\n value={editingUser.email}\n onChange={(e) => setEditingUser({ ...editingUser, email: e.target.value })}\n required\n data-testid=\"edit-user-email\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"0.5rem\" }}>\n <button type=\"button\" onClick={() => setEditingUser(null)} style={{ padding: \"6px 12px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", cursor: \"pointer\" }}>\n {labels.usersView.cancel}\n </button>\n <button type=\"submit\" data-testid=\"submit-edit-user\" style={{ padding: \"6px 12px\", borderRadius: \"4px\", backgroundColor: \"#0284c7\", color: \"#ffffff\", border: \"none\", cursor: \"pointer\" }}>\n {labels.usersView.save}\n </button>\n </div>\n </form>\n </div>\n )}\n </section>\n )\n}\n\n/**\n * View for managing user groups (create, edit, delete).\n * ユーザーグループ管理ビュー (作成・編集・削除)。\n */\nexport function GroupsView() {\n const data = useLoaderData() as UserAdminLoaderData\n const fetcher = useFetcher()\n const labels = data.labels ?? defaultUserAdminLabels\n\n const [searchQuery, setSearchQuery] = useState(\"\")\n const [isCreateOpen, setIsCreateOpen] = useState(false)\n const [editingGroup, setEditingGroup] = useState<AuthGroup | null>(null)\n\n const [newGroupKey, setNewGroupKey] = useState(\"\")\n const [newName, setNewName] = useState(\"\")\n const [newDescription, setNewDescription] = useState(\"\")\n\n const filteredGroups = data.groups.filter((g) => g.name.toLowerCase().includes(searchQuery.toLowerCase()) || g.groupKey.toLowerCase().includes(searchQuery.toLowerCase()))\n\n const handleCreateGroup = (e: React.FormEvent) => {\n e.preventDefault()\n if (!(newGroupKey && newName)) return\n fetcher.submit({ intent: \"createGroup\", groupKey: newGroupKey, name: newName, description: newDescription }, { method: \"post\" })\n setNewGroupKey(\"\")\n setNewName(\"\")\n setNewDescription(\"\")\n setIsCreateOpen(false)\n }\n\n const handleUpdateGroup = (e: React.FormEvent) => {\n e.preventDefault()\n if (!editingGroup) return\n fetcher.submit({ intent: \"updateGroup\", groupId: String(editingGroup.id), name: editingGroup.name, description: editingGroup.description ?? \"\" }, { method: \"post\" })\n setEditingGroup(null)\n }\n\n const handleDeleteGroup = (groupId: number) => {\n if (!window.confirm(labels.groupsView.confirmDelete)) return\n fetcher.submit({ intent: \"deleteGroup\", groupId: String(groupId) }, { method: \"post\" })\n }\n\n return (\n <section data-testid=\"groups-view-section\" style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}>\n <ActionError fetcher={fetcher} />\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", backgroundColor: \"#ffffff\", padding: \"1rem\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\" }}>\n <input\n type=\"text\"\n placeholder={labels.groupsView.searchPlaceholder}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n data-testid=\"groups-search-input\"\n style={{ padding: \"6px 12px\", borderRadius: \"6px\", border: \"1px solid #cbd5e1\", fontSize: \"0.85rem\", width: \"260px\" }}\n />\n <button type=\"button\" onClick={() => setIsCreateOpen(true)} data-testid=\"groups-add-button\" style={{ padding: \"6px 14px\", borderRadius: \"6px\", backgroundColor: \"#4338ca\", color: \"#ffffff\", border: \"none\", fontSize: \"0.85rem\", fontWeight: 600, cursor: \"pointer\" }}>\n {labels.groupsView.addGroup}\n </button>\n </div>\n\n <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fill, minmax(280px, 1fr))\", gap: \"1rem\" }}>\n {filteredGroups.map((g) => {\n const memberCount = data.groupMembers.filter((m) => m.groupId === g.id).length\n return (\n <div key={g.id} style={{ backgroundColor: \"#ffffff\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\", padding: \"1rem\", display: \"flex\", flexDirection: \"column\", justifyContent: \"space-between\" }} data-testid={`group-card-${g.id}`}>\n <div>\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", marginBottom: \"0.5rem\" }}>\n <h4 style={{ margin: 0, fontSize: \"1rem\", color: \"#0f172a\" }}>{g.name}</h4>\n <span style={{ fontSize: \"0.72rem\", backgroundColor: \"#e0e7ff\", color: \"#4338ca\", padding: \"2px 6px\", borderRadius: \"4px\", fontFamily: \"monospace\" }}>{g.groupKey}</span>\n </div>\n <p style={{ margin: \"0 0 0.8rem 0\", fontSize: \"0.82rem\", color: \"#64748b\", minHeight: \"2.4em\" }}>{g.description || labels.groupsView.noDescription}</p>\n </div>\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", borderTop: \"1px solid #f1f5f9\", paddingTop: \"0.6rem\" }}>\n <span style={{ fontSize: \"0.78rem\", color: \"#475569\" }}>\n 👥 メンバー: <strong>{memberCount}</strong> 人\n </span>\n <div>\n <button\n type=\"button\"\n onClick={() => setEditingGroup(g)}\n data-testid={`edit-group-btn-${g.id}`}\n style={{ padding: \"3px 8px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", fontSize: \"0.75rem\", cursor: \"pointer\", marginRight: \"4px\" }}>\n {labels.usersView.edit}\n </button>\n <button\n type=\"button\"\n onClick={() => handleDeleteGroup(g.id)}\n data-testid={`delete-group-btn-${g.id}`}\n style={{ padding: \"3px 8px\", borderRadius: \"4px\", border: \"1px solid #fee2e2\", backgroundColor: \"#fff5f5\", color: \"#b91c1c\", fontSize: \"0.75rem\", cursor: \"pointer\" }}>\n 削除\n </button>\n </div>\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Create Group Modal */}\n {isCreateOpen && (\n <div style={{ position: \"fixed\", inset: 0, backgroundColor: \"rgba(0,0,0,0.4)\", display: \"flex\", justifyContent: \"center\", alignItems: \"center\", zIndex: 100 }} data-testid=\"group-create-modal\">\n <form onSubmit={handleCreateGroup} style={{ backgroundColor: \"#ffffff\", padding: \"1.5rem\", borderRadius: \"8px\", width: \"360px\", boxShadow: \"0 10px 25px rgba(0,0,0,0.15)\" }}>\n <h3 style={{ margin: \"0 0 1rem 0\", fontSize: \"1.1rem\" }}>{labels.groupsView.addGroup}</h3>\n <div style={{ marginBottom: \"1rem\" }}>\n <label htmlFor=\"create-group-key-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.groupsView.groupKey}\n </label>\n <input\n id=\"create-group-key-input\"\n type=\"text\"\n value={newGroupKey}\n onChange={(e) => setNewGroupKey(e.target.value)}\n placeholder={labels.groupsView.groupKeyPlaceholder}\n required\n data-testid=\"create-group-key\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ marginBottom: \"1rem\" }}>\n <label htmlFor=\"create-group-name-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.groupsView.name}\n </label>\n <input\n id=\"create-group-name-input\"\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n placeholder={labels.groupsView.namePlaceholder}\n required\n data-testid=\"create-group-name\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ marginBottom: \"1.2rem\" }}>\n <label htmlFor=\"create-group-desc-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.groupsView.description}\n </label>\n <textarea\n id=\"create-group-desc-input\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n data-testid=\"create-group-description\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\", height: \"60px\" }}\n />\n </div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"0.5rem\" }}>\n <button type=\"button\" onClick={() => setIsCreateOpen(false)} style={{ padding: \"6px 12px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", cursor: \"pointer\" }}>\n キャンセル\n </button>\n <button type=\"submit\" data-testid=\"submit-create-group\" style={{ padding: \"6px 12px\", borderRadius: \"4px\", backgroundColor: \"#4338ca\", color: \"#ffffff\", border: \"none\", cursor: \"pointer\" }}>\n 作成\n </button>\n </div>\n </form>\n </div>\n )}\n\n {/* Edit Group Modal */}\n {editingGroup && (\n <div style={{ position: \"fixed\", inset: 0, backgroundColor: \"rgba(0,0,0,0.4)\", display: \"flex\", justifyContent: \"center\", alignItems: \"center\", zIndex: 100 }} data-testid=\"group-edit-modal\">\n <form onSubmit={handleUpdateGroup} style={{ backgroundColor: \"#ffffff\", padding: \"1.5rem\", borderRadius: \"8px\", width: \"360px\", boxShadow: \"0 10px 25px rgba(0,0,0,0.15)\" }}>\n <h3 style={{ margin: \"0 0 1rem 0\", fontSize: \"1.1rem\" }}>{labels.groupsView.editGroup}</h3>\n <div style={{ marginBottom: \"1rem\" }}>\n <label htmlFor=\"edit-group-name-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.groupsView.name}\n </label>\n <input\n id=\"edit-group-name-input\"\n type=\"text\"\n value={editingGroup.name}\n onChange={(e) => setEditingGroup({ ...editingGroup, name: e.target.value })}\n required\n data-testid=\"edit-group-name\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ marginBottom: \"1.2rem\" }}>\n <label htmlFor=\"edit-group-desc-input\" style={{ display: \"block\", fontSize: \"0.8rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.groupsView.description}\n </label>\n <textarea\n id=\"edit-group-desc-input\"\n value={editingGroup.description ?? \"\"}\n onChange={(e) => setEditingGroup({ ...editingGroup, description: e.target.value })}\n data-testid=\"edit-group-description\"\n style={{ width: \"100%\", padding: \"6px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", boxSizing: \"border-box\", height: \"60px\" }}\n />\n </div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: \"0.5rem\" }}>\n <button type=\"button\" onClick={() => setEditingGroup(null)} style={{ padding: \"6px 12px\", borderRadius: \"4px\", border: \"1px solid #cbd5e1\", backgroundColor: \"#ffffff\", cursor: \"pointer\" }}>\n {labels.groupsView.cancel}\n </button>\n <button type=\"submit\" data-testid=\"submit-edit-group\" style={{ padding: \"6px 12px\", borderRadius: \"4px\", backgroundColor: \"#4338ca\", color: \"#ffffff\", border: \"none\", cursor: \"pointer\" }}>\n {labels.groupsView.save}\n </button>\n </div>\n </form>\n </div>\n )}\n </section>\n )\n}\n\n/**\n * View for managing group member assignments.\n * グループメンバー割り当て管理ビュー。\n */\nexport function GroupMembersView() {\n const data = useLoaderData() as UserAdminLoaderData\n const fetcher = useFetcher()\n const labels = data.labels ?? defaultUserAdminLabels\n\n const [selectedGroupId, setSelectedGroupId] = useState<number>(data.groups[0]?.id ?? 0)\n\n const currentGroup = data.groups.find((g) => g.id === selectedGroupId)\n const currentMemberUserIds = new Set(data.groupMembers.filter((m) => m.groupId === selectedGroupId).map((m) => m.userId))\n\n const handleToggleMember = (userId: number, isMember: boolean) => {\n if (isMember) {\n fetcher.submit({ intent: \"removeGroupMember\", groupId: String(selectedGroupId), userId: String(userId) }, { method: \"post\" })\n } else {\n fetcher.submit({ intent: \"addGroupMember\", groupId: String(selectedGroupId), userId: String(userId) }, { method: \"post\" })\n }\n }\n\n return (\n <section data-testid=\"group-members-view-section\" style={{ display: \"grid\", gridTemplateColumns: \"240px 1fr\", gap: \"1.2rem\" }}>\n <ActionError fetcher={fetcher} />\n <div style={{ backgroundColor: \"#ffffff\", padding: \"1rem\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\" }}>\n <h4 style={{ margin: \"0 0 0.8rem 0\", fontSize: \"0.9rem\", color: \"#475569\" }}>{labels.groupMembersView.selectGroup}</h4>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.4rem\" }}>\n {data.groups.map((g) => {\n const isSelected = g.id === selectedGroupId\n return (\n <button\n type=\"button\"\n key={g.id}\n onClick={() => setSelectedGroupId(g.id)}\n data-testid={`select-group-btn-${g.id}`}\n style={{\n textAlign: \"left\",\n padding: \"8px 10px\",\n borderRadius: \"6px\",\n border: isSelected ? \"1px solid #6366f1\" : \"1px solid #e2e8f0\",\n backgroundColor: isSelected ? \"#e0e7ff\" : \"#ffffff\",\n color: isSelected ? \"#3730a3\" : \"#0f172a\",\n cursor: \"pointer\",\n fontWeight: isSelected ? 600 : 400,\n fontSize: \"0.85rem\",\n }}>\n {g.name}\n </button>\n )\n })}\n </div>\n </div>\n\n <div style={{ backgroundColor: \"#ffffff\", padding: \"1.2rem\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\" }}>\n {currentGroup ? (\n <>\n <div style={{ marginBottom: \"1rem\", borderBottom: \"1px solid #e2e8f0\", paddingBottom: \"0.8rem\" }}>\n <h3 style={{ margin: 0, fontSize: \"1.1rem\", color: \"#0f172a\" }}>{currentGroup.name} の所属メンバー</h3>\n <span style={{ fontSize: \"0.8rem\", color: \"#64748b\" }}>グループキー: {currentGroup.groupKey}</span>\n </div>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\" }}>\n {data.users.map((u) => {\n const isMember = currentMemberUserIds.has(u.id)\n return (\n <div\n key={u.id}\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n padding: \"8px 12px\",\n borderRadius: \"6px\",\n border: \"1px solid #f1f5f9\",\n backgroundColor: isMember ? \"#f0fdf4\" : \"#ffffff\",\n }}\n data-testid={`member-row-${u.id}`}>\n <div>\n <span style={{ fontWeight: 600, fontSize: \"0.88rem\", color: \"#0f172a\", marginRight: \"8px\" }}>{u.displayName}</span>\n <span style={{ fontSize: \"0.78rem\", color: \"#64748b\" }}>({u.email})</span>\n </div>\n <button\n type=\"button\"\n onClick={() => handleToggleMember(u.id, isMember)}\n data-testid={`toggle-member-btn-${u.id}`}\n style={{\n padding: \"4px 12px\",\n borderRadius: \"4px\",\n border: isMember ? \"1px solid #b91c1c\" : \"1px solid #0284c7\",\n backgroundColor: isMember ? \"#ffffff\" : \"#0284c7\",\n color: isMember ? \"#b91c1c\" : \"#ffffff\",\n fontSize: \"0.78rem\",\n fontWeight: 600,\n cursor: \"pointer\",\n }}>\n {isMember ? labels.groupMembersView.actionRemove : labels.groupMembersView.actionAdd}\n </button>\n </div>\n )\n })}\n </div>\n </>\n ) : (\n <div style={{ color: \"#94a3b8\", textAlign: \"center\", padding: \"2rem\" }}>{labels.groupMembersView.selectPrompt}</div>\n )}\n </div>\n </section>\n )\n}\n\n/**\n * View for managing authentication allowlist entries.\n * アクセス許可リスト (Allowlist) 管理ビュー。\n */\nexport function AllowlistView() {\n const data = useLoaderData() as UserAdminLoaderData\n const fetcher = useFetcher()\n const labels = data.labels ?? defaultUserAdminLabels\n\n const [pattern, setPattern] = useState(\"\")\n const [type, setType] = useState<\"email\" | \"domain\">(\"email\")\n const [description, setDescription] = useState(\"\")\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault()\n if (!pattern) return\n fetcher.submit({ intent: \"addAllowlistEntry\", pattern, type, description }, { method: \"post\" })\n setPattern(\"\")\n setDescription(\"\")\n }\n\n const handleRemoveEntry = (id: number) => {\n fetcher.submit({ intent: \"removeAllowlistEntry\", entryId: String(id) }, { method: \"post\" })\n }\n\n return (\n <section data-testid=\"allowlist-view-section\" style={{ display: \"flex\", flexDirection: \"column\", gap: \"1.2rem\" }}>\n <ActionError fetcher={fetcher} />\n <form onSubmit={handleAddEntry} style={{ backgroundColor: \"#ffffff\", padding: \"1.2rem\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\", display: \"flex\", flexWrap: \"wrap\", gap: \"0.8rem\", alignItems: \"flex-end\" }}>\n <div style={{ flex: 2, minWidth: \"220px\" }}>\n <label htmlFor=\"allowlist-pattern-input-field\" style={{ display: \"block\", fontSize: \"0.78rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.allowlistView.pattern}\n </label>\n <input\n id=\"allowlist-pattern-input-field\"\n type=\"text\"\n value={pattern}\n onChange={(e) => setPattern(e.target.value)}\n required\n data-testid=\"allowlist-pattern-input\"\n style={{ width: \"100%\", padding: \"6px 10px\", borderRadius: \"6px\", border: \"1px solid #cbd5e1\", fontSize: \"0.85rem\", boxSizing: \"border-box\" }}\n />\n </div>\n <div style={{ flex: 1, minWidth: \"140px\" }}>\n <label htmlFor=\"allowlist-type-select-field\" style={{ display: \"block\", fontSize: \"0.78rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.allowlistView.type}\n </label>\n <select\n id=\"allowlist-type-select-field\"\n value={type}\n onChange={(e) => setType(e.target.value as \"email\" | \"domain\")}\n data-testid=\"allowlist-type-select\"\n style={{ width: \"100%\", padding: \"6px 10px\", borderRadius: \"6px\", border: \"1px solid #cbd5e1\", fontSize: \"0.85rem\", boxSizing: \"border-box\" }}>\n <option value=\"email\">{labels.allowlistView.emailType}</option>\n <option value=\"domain\">{labels.allowlistView.domainType}</option>\n </select>\n </div>\n <div style={{ flex: 2, minWidth: \"200px\" }}>\n <label htmlFor=\"allowlist-desc-input-field\" style={{ display: \"block\", fontSize: \"0.78rem\", color: \"#475569\", marginBottom: \"4px\" }}>\n {labels.allowlistView.description}\n </label>\n <input\n id=\"allowlist-desc-input-field\"\n type=\"text\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n data-testid=\"allowlist-desc-input\"\n style={{ width: \"100%\", padding: \"6px 10px\", borderRadius: \"6px\", border: \"1px solid #cbd5e1\", fontSize: \"0.85rem\", boxSizing: \"border-box\" }}\n />\n </div>\n <button type=\"submit\" data-testid=\"submit-allowlist-add\" style={{ padding: \"7px 16px\", borderRadius: \"6px\", backgroundColor: \"#047857\", color: \"#ffffff\", border: \"none\", fontSize: \"0.85rem\", fontWeight: 600, cursor: \"pointer\" }}>\n {labels.allowlistView.addEntry}\n </button>\n </form>\n\n <div style={{ backgroundColor: \"#ffffff\", borderRadius: \"8px\", border: \"1px solid #e2e8f0\", overflow: \"hidden\" }}>\n <table style={{ width: \"100%\", borderCollapse: \"collapse\", fontSize: \"0.85rem\", textAlign: \"left\" }}>\n <thead>\n <tr style={{ backgroundColor: \"#f8fafc\", borderBottom: \"1px solid #e2e8f0\", color: \"#475569\" }}>\n <th style={{ padding: \"10px 14px\" }}>ID</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.allowlistView.pattern}</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.allowlistView.type}</th>\n <th style={{ padding: \"10px 14px\" }}>{labels.allowlistView.description}</th>\n <th style={{ padding: \"10px 14px\", textAlign: \"right\" }}>{labels.allowlistView.actions}</th>\n </tr>\n </thead>\n <tbody>\n {data.allowlist.map((item) => (\n <tr key={item.id} style={{ borderBottom: \"1px solid #f1f5f9\" }} data-testid={`allowlist-row-${item.id}`}>\n <td style={{ padding: \"10px 14px\", color: \"#64748b\" }}>#{item.id}</td>\n <td style={{ padding: \"10px 14px\", fontWeight: 600, color: \"#0f172a\", fontFamily: \"monospace\" }}>{item.pattern}</td>\n <td style={{ padding: \"10px 14px\" }}>\n <span style={{ fontSize: \"0.75rem\", padding: \"2px 8px\", borderRadius: \"10px\", backgroundColor: item.type === \"email\" ? \"#e0f2fe\" : \"#fef3c7\", color: item.type === \"email\" ? \"#0369a1\" : \"#b45309\", fontWeight: 600 }}>\n {item.type === \"email\" ? labels.allowlistView.emailTypeShort : labels.allowlistView.domainTypeShort}\n </span>\n </td>\n <td style={{ padding: \"10px 14px\", color: \"#475569\" }}>{item.description || \"-\"}</td>\n <td style={{ padding: \"10px 14px\", textAlign: \"right\" }}>\n {/* 変更不能な供給元 (環境変数など) は削除ボタンを出さない */}\n {item.readOnly ? (\n <span data-testid={`readonly-allowlist-${item.id}`} style={{ fontSize: \"0.75rem\", color: \"#94a3b8\" }}>\n {labels.allowlistView.readOnly}\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={() => handleRemoveEntry(item.id)}\n data-testid={`remove-allowlist-btn-${item.id}`}\n style={{ padding: \"3px 8px\", borderRadius: \"4px\", border: \"1px solid #fee2e2\", backgroundColor: \"#fff5f5\", color: \"#b91c1c\", fontSize: \"0.78rem\", cursor: \"pointer\" }}>\n 削除\n </button>\n )}\n </td>\n </tr>\n ))}\n {data.allowlist.length === 0 && (\n <tr>\n <td colSpan={5} style={{ padding: \"2rem\", textAlign: \"center\", color: \"#94a3b8\" }}>\n {labels.allowlistView.empty}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n </section>\n )\n}\n\nconst SEGMENT_VIEWS: Record<string, () => React.ReactElement> = {\n users: UsersView,\n groups: GroupsView,\n group_members: GroupMembersView,\n allowlist: AllowlistView,\n}\n\nexport type AuthUserAdminAppViewProps = UserAdminShellProps\n\n/**\n * Top-level application view component for the user administration UI (`/users/*`).\n * ユーザー管理 UI 全体を描画するトップレベルコンポーネント。\n */\nexport function AuthUserAdminAppView({ renderHeader }: AuthUserAdminAppViewProps = {}) {\n const data = useLoaderData() as UserAdminLoaderData | undefined\n const segment = data?.segment ?? \"users\"\n const View = SEGMENT_VIEWS[segment] ?? UsersView\n\n return (\n <UserAdminShell renderHeader={renderHeader}>\n <View />\n </UserAdminShell>\n )\n}\n"],"mappings":"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","names":["admin_exports","__export","AllowlistView","AuthUserAdminAppView","GroupMembersView","GroupsView","UserAdminShell","UsersView","createInMemoryUserAdminStore","createUserAdminApp","defaultUserAdminLabels","jaUserAdminLabels","resolveUserAdminLabels","__toCommonJS","defaultUserAdminLabels","jaUserAdminLabels","resolveUserAdminLabels","over","d","createInMemoryUserAdminStore","initial","users","groups","groupMembers","allowlist","data","newUser","u","id","idx","isActive","newGroup","g","m","groupId","userId","existing","newMember","newEntry","a","INTENT_ACCESS","requireId","formData","field","raw","n","requireText","value","optionalText","createUserAdminApp","config","labels","resolveUserAdminLabels","loader","request","pathSegments","segment","myPermissions","action","intent","access","dispatch","error","rawActive","type","import_react_router","import_jsx_runtime","GroupPill","label","summary","UserAdminShell","children","renderHeader","data","segment","labels","defaultUserAdminLabels","myPermissions","tabs","permEntries","displayEntries","getSummary","perm","p","allowed","canWrite","canRead","annotation","resKey","idx","tab","isActive","import_react","import_react_router","import_jsx_runtime","ActionError","fetcher","result","UsersView","data","labels","defaultUserAdminLabels","searchQuery","setSearchQuery","editingUser","setEditingUser","isCreateOpen","setIsCreateOpen","newDisplayName","setNewDisplayName","newEmail","setNewEmail","filteredUsers","handleToggleActive","user","handleCreateUser","e","handleUpdateUser","GroupsView","editingGroup","setEditingGroup","newGroupKey","setNewGroupKey","newName","setNewName","newDescription","setNewDescription","filteredGroups","g","handleCreateGroup","handleUpdateGroup","handleDeleteGroup","groupId","memberCount","m","GroupMembersView","selectedGroupId","setSelectedGroupId","currentGroup","currentMemberUserIds","handleToggleMember","userId","isMember","isSelected","u","AllowlistView","pattern","setPattern","type","setType","description","setDescription","handleAddEntry","handleRemoveEntry","id","item","SEGMENT_VIEWS","AuthUserAdminAppView","renderHeader","segment","View","UserAdminShell"]}
package/dist/admin.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var v={title:"Users & Groups",heading:"\u{1F464} User administration",tabs:{users:"\u{1F464} Users",groups:"\u{1F465} Groups",groupMembers:"\u{1F517} Members",allowlist:"\u2709\uFE0F Allowlist"},usersView:{title:"Users",searchPlaceholder:"Search by name or email...",addUser:"\uFF0B Add user",editUser:"Edit user",id:"ID",displayName:"Name",email:"Email",status:"Status",active:"Active",inactive:"Inactive",actions:"Actions",edit:"Edit",activate:"Activate",deactivate:"Deactivate",empty:"No matching users",save:"Save",cancel:"Cancel"},groupsView:{title:"Groups",searchPlaceholder:"Search by group name or key...",addGroup:"\uFF0B Add group",editGroup:"Edit group",id:"ID",groupKey:"Group key",name:"Name",description:"Description",memberCount:"Members",status:"Status",actions:"Actions",edit:"Edit",delete:"Delete",confirmDelete:"Delete this group?",noDescription:"No description",groupKeyPlaceholder:"e.g. dev-team",namePlaceholder:"e.g. Development team",empty:"No groups registered",save:"Save",cancel:"Cancel"},groupMembersView:{selectGroup:"Select a group",selectPrompt:"Select a group to manage its members",actionAdd:"Add",actionRemove:"Remove"},allowlistView:{title:"Sign-in allowlist",addEntry:"\uFF0B Add pattern",pattern:"Pattern (e.g. @example.com or user@example.com)",type:"Type",emailType:"Exact email match",domainType:"Domain match",emailTypeShort:"Email",domainTypeShort:"Domain",description:"Description",actions:"Actions",readOnly:"Read-only",delete:"Delete",empty:"No allowlist entries registered",save:"Add",cancel:"Cancel"}},O={title:"\u30E6\u30FC\u30B6\u30FC\u30FB\u30B0\u30EB\u30FC\u30D7\u7BA1\u7406",heading:"\u{1F464} \u30E6\u30FC\u30B6\u30FC\u7BA1\u7406",tabs:{users:"\u{1F464} \u30E6\u30FC\u30B6\u30FC\u4E00\u89A7",groups:"\u{1F465} \u30E6\u30FC\u30B6\u30FC\u30B0\u30EB\u30FC\u30D7",groupMembers:"\u{1F517} \u30B0\u30EB\u30FC\u30D7\u30E1\u30F3\u30D0\u30FC",allowlist:"\u2709\uFE0F \u30A2\u30AF\u30BB\u30B9\u8A31\u53EF\u30EA\u30B9\u30C8"},usersView:{title:"\u30E6\u30FC\u30B6\u30FC\u7BA1\u7406",searchPlaceholder:"\u8868\u793A\u540D\u3084 Email \u3067\u691C\u7D22...",addUser:"\uFF0B \u30E6\u30FC\u30B6\u30FC\u8FFD\u52A0",editUser:"\u30E6\u30FC\u30B6\u30FC\u306E\u7DE8\u96C6",id:"ID",displayName:"\u8868\u793A\u540D",email:"\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9",status:"\u30B9\u30C6\u30FC\u30BF\u30B9",active:"\u6709\u52B9",inactive:"\u7121\u52B9",actions:"\u64CD\u4F5C",edit:"\u7DE8\u96C6",activate:"\u6709\u52B9\u5316",deactivate:"\u7121\u52B9\u5316",empty:"\u8A72\u5F53\u3059\u308B\u30E6\u30FC\u30B6\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",save:"\u4FDD\u5B58",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"},groupsView:{title:"\u30E6\u30FC\u30B6\u30FC\u30B0\u30EB\u30FC\u30D7\u7BA1\u7406",searchPlaceholder:"\u30B0\u30EB\u30FC\u30D7\u540D\u3084\u30AD\u30FC\u3067\u691C\u7D22...",addGroup:"\uFF0B \u30B0\u30EB\u30FC\u30D7\u4F5C\u6210",editGroup:"\u30B0\u30EB\u30FC\u30D7\u306E\u7DE8\u96C6",id:"ID",groupKey:"\u30B0\u30EB\u30FC\u30D7\u30AD\u30FC",name:"\u30B0\u30EB\u30FC\u30D7\u540D",description:"\u8AAC\u660E",memberCount:"\u30E1\u30F3\u30D0\u30FC\u6570",status:"\u30B9\u30C6\u30FC\u30BF\u30B9",actions:"\u64CD\u4F5C",edit:"\u7DE8\u96C6",delete:"\u524A\u9664",confirmDelete:"\u3053\u306E\u30B0\u30EB\u30FC\u30D7\u3092\u524A\u9664\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B\uFF1F",noDescription:"\u8AAC\u660E\u306A\u3057",groupKeyPlaceholder:"\u4F8B: dev-team",namePlaceholder:"\u4F8B: \u958B\u767A\u30C1\u30FC\u30E0",empty:"\u30B0\u30EB\u30FC\u30D7\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093",save:"\u4FDD\u5B58",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"},groupMembersView:{selectGroup:"\u30B0\u30EB\u30FC\u30D7\u9078\u629E",selectPrompt:"\u30B0\u30EB\u30FC\u30D7\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",actionAdd:"\u8FFD\u52A0",actionRemove:"\u9664\u5916"},allowlistView:{title:"\u30A2\u30AF\u30BB\u30B9\u8A31\u53EF\u30EA\u30B9\u30C8 (Allowlist)",addEntry:"\uFF0B \u8A31\u53EF\u30D1\u30BF\u30FC\u30F3\u8FFD\u52A0",pattern:"\u30D1\u30BF\u30FC\u30F3 (\u4F8B: @example.com \u307E\u305F\u306F user@example.com)",type:"\u7A2E\u5225",emailType:"\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u5B8C\u5168\u4E00\u81F4",domainType:"\u30C9\u30E1\u30A4\u30F3\u4E00\u81F4",emailTypeShort:"Email \u5B8C\u5168\u4E00\u81F4",domainTypeShort:"\u30C9\u30E1\u30A4\u30F3\u4E00\u81F4",description:"\u8AAC\u660E / \u5099\u8003",actions:"\u64CD\u4F5C",readOnly:"\u5909\u66F4\u4E0D\u53EF",delete:"\u524A\u9664",empty:"\u30A2\u30ED\u30EF\u30EA\u30B9\u30C8\u30A8\u30F3\u30C8\u30EA\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093",save:"\u767B\u9332",cancel:"\u30AD\u30E3\u30F3\u30BB\u30EB"}};function z(o){let s=v;return o?{title:o.title??s.title,heading:o.heading??s.heading,tabs:{...s.tabs,...o.tabs},usersView:{...s.usersView,...o.usersView},groupsView:{...s.groupsView,...o.groupsView},groupMembersView:{...s.groupMembersView,...o.groupMembersView},allowlistView:{...s.allowlistView,...o.allowlistView}}:s}function Q(o){let s=o?.users??[{id:1,email:"admin@example.com",displayName:"\u30B7\u30B9\u30C6\u30E0\u7BA1\u7406\u8005",isActive:!0},{id:2,email:"user1@example.com",displayName:"\u958B\u767A \u592A\u90CE",isActive:!0},{id:3,email:"user2@example.com",displayName:"\u55B6\u696D \u82B1\u5B50",isActive:!0}],t=o?.groups??[{id:1,groupKey:"dev-team",name:"\u958B\u767A\u30C1\u30FC\u30E0",description:"\u30B7\u30B9\u30C6\u30E0\u958B\u767A\u62C5\u5F53\u30B0\u30EB\u30FC\u30D7",isActive:!0},{id:2,groupKey:"sales-dept",name:"\u55B6\u696D\u90E8",description:"\u55B6\u696D\u30BD\u30EA\u30E5\u30FC\u30B7\u30E7\u30F3\u62C5\u5F53",isActive:!0}],u=o?.groupMembers??[{id:1,groupId:1,userId:2},{id:2,groupId:2,userId:3}],g=o?.allowlist??[{id:1,pattern:"admin@example.com",type:"email",description:"\u521D\u671F\u7BA1\u7406\u8005"},{id:2,pattern:"@example.com",type:"domain",description:"\u793E\u5185\u30C9\u30E1\u30A4\u30F3"}];return{async listUsers(){return[...s]},async createUser(i){let r={id:Math.max(...s.map(n=>n.id),0)+1,email:i.email,displayName:i.displayName,isActive:!0};return s.push(r),r},async updateUser(i,r){let n=s.findIndex(d=>d.id===i);if(n===-1)throw new Error(`User not found: ${i}`);return s[n]={...s[n],displayName:r.displayName,email:r.email},s[n]},async toggleUserActive(i,r){let n=s.findIndex(d=>d.id===i);if(n===-1)throw new Error(`User not found: ${i}`);return s[n]={...s[n],isActive:r},s[n]},async listGroups(){return[...t]},async createGroup(i){let r={id:Math.max(...t.map(n=>n.id),0)+1,groupKey:i.groupKey,name:i.name,description:i.description,isActive:!0};return t.push(r),r},async updateGroup(i,r){let n=t.findIndex(d=>d.id===i);if(n===-1)throw new Error(`Group not found: ${i}`);return t[n]={...t[n],name:r.name,description:r.description},t[n]},async deleteGroup(i){t=t.filter(r=>r.id!==i),u=u.filter(r=>r.groupId!==i)},async listGroupMembers(){return[...u]},async addGroupMember(i,r){let n=u.find(p=>p.groupId===i&&p.userId===r);if(n)return n;let d={id:Math.max(...u.map(p=>p.id),0)+1,groupId:i,userId:r};return u.push(d),d},async removeGroupMember(i,r){u=u.filter(n=>!(n.groupId===i&&n.userId===r))},async listAllowlist(){return[...g]},async addAllowlistEntry(i){let r={id:Math.max(...g.map(n=>n.id),0)+1,pattern:i.pattern,type:i.type,description:i.description};return g.push(r),r},async removeAllowlistEntry(i){g=g.filter(r=>r.id!==i)}}}var B=new Map([["createUser","create"],["updateUser","update"],["toggleUserActive","update"],["createGroup","create"],["updateGroup","update"],["deleteGroup","delete"],["addGroupMember","create"],["removeGroupMember","delete"],["addAllowlistEntry","create"],["removeAllowlistEntry","delete"]]);function h(o,s){let t=o.get(s),u=Number(t);if(t===null||String(t).trim()===""||!Number.isInteger(u)||u<=0)throw new Error(`Invalid or missing "${s}"`);return u}function A(o,s){let t=String(o.get(s)??"").trim();if(t==="")throw new Error(`Invalid or missing "${s}"`);return t}function I(o,s){let t=String(o.get(s)??"").trim();return t===""?void 0:t}function J(o){let s=z(o.labels);async function t({request:i}){await o.guards.requireAccess(i,{action:"read",intent:"loader"});let n=new URL(i.url).pathname.split("/").filter(Boolean),d=n[n.length-1]??"users",[p,x,l,b,y]=await Promise.all([o.store.listUsers(),o.store.listGroups(),o.store.listGroupMembers(),o.store.listAllowlist(),o.getMyPermissions?o.getMyPermissions(i).catch(()=>({})):Promise.resolve({})]);return{segment:["groups","group_members","allowlist"].includes(d)?d:"users",users:p,groups:x,groupMembers:l,allowlist:b,myPermissions:y,labels:s}}async function u({request:i}){let r=await i.formData(),n=String(r.get("intent")??""),d=B.get(n);if(!d)return{ok:!1,error:`Unknown intent: ${n}`};await o.guards.requireAccess(i,{action:d,intent:n});try{return await g(n,r)}catch(p){if(p instanceof Response)throw p;return{ok:!1,error:p instanceof Error?p.message:String(p)}}}async function g(i,r){if(i==="createUser")return await o.store.createUser({displayName:A(r,"displayName"),email:A(r,"email")}),{ok:!0};if(i==="updateUser")return await o.store.updateUser(h(r,"userId"),{displayName:A(r,"displayName"),email:A(r,"email")}),{ok:!0};if(i==="toggleUserActive"){let n=String(r.get("isActive")??"");if(n!=="true"&&n!=="false")throw new Error('Invalid or missing "isActive"');return await o.store.toggleUserActive(h(r,"userId"),n==="true"),{ok:!0}}if(i==="createGroup")return await o.store.createGroup({groupKey:A(r,"groupKey"),name:A(r,"name"),description:I(r,"description")}),{ok:!0};if(i==="updateGroup")return await o.store.updateGroup(h(r,"groupId"),{name:A(r,"name"),description:I(r,"description")}),{ok:!0};if(i==="deleteGroup")return await o.store.deleteGroup(h(r,"groupId")),{ok:!0};if(i==="addGroupMember")return await o.store.addGroupMember(h(r,"groupId"),h(r,"userId")),{ok:!0};if(i==="removeGroupMember")return await o.store.removeGroupMember(h(r,"groupId"),h(r,"userId")),{ok:!0};if(i==="addAllowlistEntry"){let n=r.get("type")==="domain"?"domain":"email";return await o.store.addAllowlistEntry({pattern:A(r,"pattern"),type:n,description:I(r,"description")}),{ok:!0}}return i==="removeAllowlistEntry"?(await o.store.removeAllowlistEntry(h(r,"entryId")),{ok:!0}):{ok:!1,error:`Unknown intent: ${i}`}}return{loader:t,action:u}}import{Link as D,useLoaderData as T}from"react-router";import{jsx as w,jsxs as C}from"react/jsx-runtime";function F({label:o,summary:s}){return C("span",{style:{display:"inline-flex",alignItems:"center",gap:"0.18rem",fontSize:"0.65rem",padding:"0.08rem 0.35rem",borderRadius:"9999px",backgroundColor:s.bgColor,color:s.color,border:`1px solid ${s.borderColor}`,fontWeight:600,lineHeight:"1.2"},children:[C("span",{children:[o,":"]}),w("span",{children:s.text})]})}function M({children:o,renderHeader:s}){let t=T(),u=t?.segment??"users",g=t?.labels??v,i=t?.myPermissions??{},r=[{key:"users",label:g.tabs.users,href:"/users"},{key:"groups",label:g.tabs.groups,href:"/users/groups"},{key:"group_members",label:g.tabs.groupMembers,href:"/users/group_members"},{key:"allowlist",label:g.tabs.allowlist,href:"/users/allowlist"}],n=Object.entries(i),d=n.length>0?n:[["user_admin",{read:!0,write:!0,delete:!0,allowedActions:["read","create","update","delete"]}]],p=l=>{let b=l,y=b?.allowedActions??[],S=b?.write??(y.includes("create")||y.includes("update")||y.includes("delete")),U=b?.read??y.includes("read");return S?{text:"\u7DE8\u96C6\u53EF",color:"#047857",bgColor:"#ecfdf5",borderColor:"#a7f3d0"}:U?{text:"\u95B2\u89A7\u306E\u307F",color:"#1d4ed8",bgColor:"#eff6ff",borderColor:"#bfdbfe"}:{text:"\u6A29\u9650\u306A\u3057",color:"#64748b",bgColor:"#f1f5f9",borderColor:"#cbd5e1"}},x=C("div",{style:{display:"flex",alignItems:"center",gap:"0.4rem"},"data-testid":"user-admin-header-annotation",children:[w("span",{style:{fontSize:"0.75rem",backgroundColor:"#e0f2fe",color:"#0369a1",padding:"2px 8px",borderRadius:"12px",fontWeight:600},children:g.heading}),C("div",{style:{display:"inline-flex",alignItems:"center",gap:"0.25rem",fontSize:"0.68rem",color:"#475569"},"data-testid":"user-admin-permissions-badge",children:[w("span",{style:{fontWeight:600,color:"#334155"},children:"\u3042\u306A\u305F\u306E\u6A29\u9650:"}),w("span",{style:{display:"inline-flex",alignItems:"center",gap:"0.18rem"},children:d.map(([l,b],y)=>w(F,{label:l==="user_admin"?"\u30E6\u30FC\u30B6\u30FC\u7BA1\u7406UI":String(l),summary:p(b)},String(l||y)))})]})]});return C("div",{"data-testid":"user-admin-shell",style:{width:"100%",minHeight:"100vh",backgroundColor:"#f8fafc",padding:"1.5rem",boxSizing:"border-box"},children:[s?s({title:g.title,annotation:x}):C("div",{style:{marginBottom:"1.5rem",display:"flex",justifyContent:"space-between",alignItems:"center"},children:[w("h1",{style:{margin:0,fontSize:"1.5rem",fontWeight:700,color:"#0f172a"},children:g.title}),x]}),w("div",{style:{display:"flex",gap:"0.5rem",marginBottom:"1.2rem",borderBottom:"2px solid #e2e8f0",paddingBottom:"0.5rem"},children:r.map(l=>{let b=u===l.key;return w(D,{to:l.href,"data-testid":`user-admin-tab-${l.key}`,style:{padding:"0.5rem 1rem",borderRadius:"6px 6px 0 0",fontSize:"0.88rem",fontWeight:b?700:500,color:b?"#0284c7":"#64748b",backgroundColor:b?"#ffffff":"transparent",textDecoration:"none",borderBottom:b?"3px solid #0284c7":"3px solid transparent",transition:"all 0.15s ease"},children:l.label},l.key)})}),w("main",{children:o})]})}import{useState as f}from"react";import{useFetcher as V,useLoaderData as k}from"react-router";import{Fragment as j,jsx as e,jsxs as a}from"react/jsx-runtime";function G({fetcher:o}){let s=o.data;return s?.ok!==!1||!s.error?null:e("div",{"data-testid":"user-admin-action-error",role:"alert",style:{padding:"0.7rem 1rem",borderRadius:"6px",border:"1px solid #fecaca",backgroundColor:"#fef2f2",color:"#b91c1c",fontSize:"0.85rem"},children:s.error})}function P(){let o=k(),s=V(),t=o.labels??v,[u,g]=f(""),[i,r]=f(null),[n,d]=f(!1),[p,x]=f(""),[l,b]=f(""),y=o.users.filter(m=>m.displayName.toLowerCase().includes(u.toLowerCase())||m.email.toLowerCase().includes(u.toLowerCase())),S=m=>{s.submit({intent:"toggleUserActive",userId:String(m.id),isActive:String(!m.isActive)},{method:"post"})},U=m=>{m.preventDefault(),p&&l&&(s.submit({intent:"createUser",displayName:p,email:l},{method:"post"}),x(""),b(""),d(!1))},R=m=>{m.preventDefault(),i&&(s.submit({intent:"updateUser",userId:String(i.id),displayName:i.displayName,email:i.email},{method:"post"}),r(null))};return a("section",{"data-testid":"users-view-section",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[e(G,{fetcher:s}),a("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[e("input",{type:"text",placeholder:t.usersView.searchPlaceholder,value:u,onChange:m=>g(m.target.value),"data-testid":"users-search-input",style:{padding:"6px 12px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",width:"260px"}}),e("button",{type:"button",onClick:()=>d(!0),"data-testid":"users-add-button",style:{padding:"6px 14px",borderRadius:"6px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.usersView.addUser})]}),e("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",overflow:"hidden"},children:a("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.85rem",textAlign:"left"},children:[e("thead",{children:a("tr",{style:{backgroundColor:"#f8fafc",borderBottom:"1px solid #e2e8f0",color:"#475569"},children:[e("th",{style:{padding:"10px 14px"},children:t.usersView.id}),e("th",{style:{padding:"10px 14px"},children:t.usersView.displayName}),e("th",{style:{padding:"10px 14px"},children:t.usersView.email}),e("th",{style:{padding:"10px 14px"},children:t.usersView.status}),e("th",{style:{padding:"10px 14px",textAlign:"right"},children:t.usersView.actions})]})}),a("tbody",{children:[y.map(m=>a("tr",{style:{borderBottom:"1px solid #f1f5f9"},"data-testid":`user-row-${m.id}`,children:[a("td",{style:{padding:"10px 14px",color:"#64748b"},children:["#",m.id]}),e("td",{style:{padding:"10px 14px",fontWeight:600,color:"#0f172a"},children:m.displayName}),e("td",{style:{padding:"10px 14px",color:"#475569"},children:m.email}),e("td",{style:{padding:"10px 14px"},children:e("span",{style:{fontSize:"0.75rem",padding:"2px 8px",borderRadius:"10px",backgroundColor:m.isActive?"#dcfce7":"#fee2e2",color:m.isActive?"#15803d":"#b91c1c",fontWeight:600},children:m.isActive?t.usersView.active:t.usersView.inactive})}),a("td",{style:{padding:"10px 14px",textAlign:"right"},children:[e("button",{type:"button",onClick:()=>r(m),"data-testid":`edit-user-btn-${m.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.78rem",cursor:"pointer",marginRight:"6px"},children:t.usersView.edit}),e("button",{type:"button",onClick:()=>S(m),"data-testid":`toggle-user-btn-${m.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.78rem",cursor:"pointer",color:m.isActive?"#b91c1c":"#15803d"},children:m.isActive?t.usersView.deactivate:t.usersView.activate})]})]},m.id)),y.length===0&&e("tr",{children:e("td",{colSpan:5,style:{padding:"2rem",textAlign:"center",color:"#94a3b8"},children:t.usersView.empty})})]})]})}),n&&e("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"user-create-modal",children:a("form",{onSubmit:U,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[e("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.usersView.addUser}),a("div",{style:{marginBottom:"1rem"},children:[e("label",{htmlFor:"create-user-display-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.displayName}),e("input",{id:"create-user-display-name-input",type:"text",value:p,onChange:m=>x(m.target.value),required:!0,"data-testid":"create-user-display-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{marginBottom:"1.2rem"},children:[e("label",{htmlFor:"create-user-email-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.email}),e("input",{id:"create-user-email-input",type:"email",value:l,onChange:m=>b(m.target.value),required:!0,"data-testid":"create-user-email",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[e("button",{type:"button",onClick:()=>d(!1),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:"\u30AD\u30E3\u30F3\u30BB\u30EB"}),e("button",{type:"submit","data-testid":"submit-create-user",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",cursor:"pointer"},children:"\u4F5C\u6210"})]})]})}),i&&e("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"user-edit-modal",children:a("form",{onSubmit:R,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[e("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.usersView.editUser}),a("div",{style:{marginBottom:"1rem"},children:[e("label",{htmlFor:"edit-user-display-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.displayName}),e("input",{id:"edit-user-display-name-input",type:"text",value:i.displayName,onChange:m=>r({...i,displayName:m.target.value}),required:!0,"data-testid":"edit-user-display-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{marginBottom:"1.2rem"},children:[e("label",{htmlFor:"edit-user-email-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.usersView.email}),e("input",{id:"edit-user-email-input",type:"email",value:i.email,onChange:m=>r({...i,email:m.target.value}),required:!0,"data-testid":"edit-user-email",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[e("button",{type:"button",onClick:()=>r(null),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:t.usersView.cancel}),e("button",{type:"submit","data-testid":"submit-edit-user",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#0284c7",color:"#ffffff",border:"none",cursor:"pointer"},children:t.usersView.save})]})]})})]})}function W(){let o=k(),s=V(),t=o.labels??v,[u,g]=f(""),[i,r]=f(!1),[n,d]=f(null),[p,x]=f(""),[l,b]=f(""),[y,S]=f(""),U=o.groups.filter(c=>c.name.toLowerCase().includes(u.toLowerCase())||c.groupKey.toLowerCase().includes(u.toLowerCase())),R=c=>{c.preventDefault(),p&&l&&(s.submit({intent:"createGroup",groupKey:p,name:l,description:y},{method:"post"}),x(""),b(""),S(""),r(!1))},m=c=>{c.preventDefault(),n&&(s.submit({intent:"updateGroup",groupId:String(n.id),name:n.name,description:n.description??""},{method:"post"}),d(null))},E=c=>{window.confirm(t.groupsView.confirmDelete)&&s.submit({intent:"deleteGroup",groupId:String(c)},{method:"post"})};return a("section",{"data-testid":"groups-view-section",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[e(G,{fetcher:s}),a("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[e("input",{type:"text",placeholder:t.groupsView.searchPlaceholder,value:u,onChange:c=>g(c.target.value),"data-testid":"groups-search-input",style:{padding:"6px 12px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",width:"260px"}}),e("button",{type:"button",onClick:()=>r(!0),"data-testid":"groups-add-button",style:{padding:"6px 14px",borderRadius:"6px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.groupsView.addGroup})]}),e("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(280px, 1fr))",gap:"1rem"},children:U.map(c=>{let N=o.groupMembers.filter(L=>L.groupId===c.id).length;return a("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",padding:"1rem",display:"flex",flexDirection:"column",justifyContent:"space-between"},"data-testid":`group-card-${c.id}`,children:[a("div",{children:[a("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"0.5rem"},children:[e("h4",{style:{margin:0,fontSize:"1rem",color:"#0f172a"},children:c.name}),e("span",{style:{fontSize:"0.72rem",backgroundColor:"#e0e7ff",color:"#4338ca",padding:"2px 6px",borderRadius:"4px",fontFamily:"monospace"},children:c.groupKey})]}),e("p",{style:{margin:"0 0 0.8rem 0",fontSize:"0.82rem",color:"#64748b",minHeight:"2.4em"},children:c.description||t.groupsView.noDescription})]}),a("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",borderTop:"1px solid #f1f5f9",paddingTop:"0.6rem"},children:[a("span",{style:{fontSize:"0.78rem",color:"#475569"},children:["\u{1F465} \u30E1\u30F3\u30D0\u30FC: ",e("strong",{children:N})," \u4EBA"]}),a("div",{children:[e("button",{type:"button",onClick:()=>d(c),"data-testid":`edit-group-btn-${c.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",fontSize:"0.75rem",cursor:"pointer",marginRight:"4px"},children:t.usersView.edit}),e("button",{type:"button",onClick:()=>E(c.id),"data-testid":`delete-group-btn-${c.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #fee2e2",backgroundColor:"#fff5f5",color:"#b91c1c",fontSize:"0.75rem",cursor:"pointer"},children:"\u524A\u9664"})]})]})]},c.id)})}),i&&e("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"group-create-modal",children:a("form",{onSubmit:R,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[e("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.groupsView.addGroup}),a("div",{style:{marginBottom:"1rem"},children:[e("label",{htmlFor:"create-group-key-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.groupKey}),e("input",{id:"create-group-key-input",type:"text",value:p,onChange:c=>x(c.target.value),placeholder:t.groupsView.groupKeyPlaceholder,required:!0,"data-testid":"create-group-key",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{marginBottom:"1rem"},children:[e("label",{htmlFor:"create-group-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.name}),e("input",{id:"create-group-name-input",type:"text",value:l,onChange:c=>b(c.target.value),placeholder:t.groupsView.namePlaceholder,required:!0,"data-testid":"create-group-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{marginBottom:"1.2rem"},children:[e("label",{htmlFor:"create-group-desc-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.description}),e("textarea",{id:"create-group-desc-input",value:y,onChange:c=>S(c.target.value),"data-testid":"create-group-description",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box",height:"60px"}})]}),a("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[e("button",{type:"button",onClick:()=>r(!1),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:"\u30AD\u30E3\u30F3\u30BB\u30EB"}),e("button",{type:"submit","data-testid":"submit-create-group",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",cursor:"pointer"},children:"\u4F5C\u6210"})]})]})}),n&&e("div",{style:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.4)",display:"flex",justifyContent:"center",alignItems:"center",zIndex:100},"data-testid":"group-edit-modal",children:a("form",{onSubmit:m,style:{backgroundColor:"#ffffff",padding:"1.5rem",borderRadius:"8px",width:"360px",boxShadow:"0 10px 25px rgba(0,0,0,0.15)"},children:[e("h3",{style:{margin:"0 0 1rem 0",fontSize:"1.1rem"},children:t.groupsView.editGroup}),a("div",{style:{marginBottom:"1rem"},children:[e("label",{htmlFor:"edit-group-name-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.name}),e("input",{id:"edit-group-name-input",type:"text",value:n.name,onChange:c=>d({...n,name:c.target.value}),required:!0,"data-testid":"edit-group-name",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box"}})]}),a("div",{style:{marginBottom:"1.2rem"},children:[e("label",{htmlFor:"edit-group-desc-input",style:{display:"block",fontSize:"0.8rem",color:"#475569",marginBottom:"4px"},children:t.groupsView.description}),e("textarea",{id:"edit-group-desc-input",value:n.description??"",onChange:c=>d({...n,description:c.target.value}),"data-testid":"edit-group-description",style:{width:"100%",padding:"6px",borderRadius:"4px",border:"1px solid #cbd5e1",boxSizing:"border-box",height:"60px"}})]}),a("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[e("button",{type:"button",onClick:()=>d(null),style:{padding:"6px 12px",borderRadius:"4px",border:"1px solid #cbd5e1",backgroundColor:"#ffffff",cursor:"pointer"},children:t.groupsView.cancel}),e("button",{type:"submit","data-testid":"submit-edit-group",style:{padding:"6px 12px",borderRadius:"4px",backgroundColor:"#4338ca",color:"#ffffff",border:"none",cursor:"pointer"},children:t.groupsView.save})]})]})})]})}function $(){let o=k(),s=V(),t=o.labels??v,[u,g]=f(o.groups[0]?.id??0),i=o.groups.find(d=>d.id===u),r=new Set(o.groupMembers.filter(d=>d.groupId===u).map(d=>d.userId)),n=(d,p)=>{p?s.submit({intent:"removeGroupMember",groupId:String(u),userId:String(d)},{method:"post"}):s.submit({intent:"addGroupMember",groupId:String(u),userId:String(d)},{method:"post"})};return a("section",{"data-testid":"group-members-view-section",style:{display:"grid",gridTemplateColumns:"240px 1fr",gap:"1.2rem"},children:[e(G,{fetcher:s}),a("div",{style:{backgroundColor:"#ffffff",padding:"1rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:[e("h4",{style:{margin:"0 0 0.8rem 0",fontSize:"0.9rem",color:"#475569"},children:t.groupMembersView.selectGroup}),e("div",{style:{display:"flex",flexDirection:"column",gap:"0.4rem"},children:o.groups.map(d=>{let p=d.id===u;return e("button",{type:"button",onClick:()=>g(d.id),"data-testid":`select-group-btn-${d.id}`,style:{textAlign:"left",padding:"8px 10px",borderRadius:"6px",border:p?"1px solid #6366f1":"1px solid #e2e8f0",backgroundColor:p?"#e0e7ff":"#ffffff",color:p?"#3730a3":"#0f172a",cursor:"pointer",fontWeight:p?600:400,fontSize:"0.85rem"},children:d.name},d.id)})})]}),e("div",{style:{backgroundColor:"#ffffff",padding:"1.2rem",borderRadius:"8px",border:"1px solid #e2e8f0"},children:i?a(j,{children:[a("div",{style:{marginBottom:"1rem",borderBottom:"1px solid #e2e8f0",paddingBottom:"0.8rem"},children:[a("h3",{style:{margin:0,fontSize:"1.1rem",color:"#0f172a"},children:[i.name," \u306E\u6240\u5C5E\u30E1\u30F3\u30D0\u30FC"]}),a("span",{style:{fontSize:"0.8rem",color:"#64748b"},children:["\u30B0\u30EB\u30FC\u30D7\u30AD\u30FC: ",i.groupKey]})]}),e("div",{style:{display:"flex",flexDirection:"column",gap:"0.5rem"},children:o.users.map(d=>{let p=r.has(d.id);return a("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"8px 12px",borderRadius:"6px",border:"1px solid #f1f5f9",backgroundColor:p?"#f0fdf4":"#ffffff"},"data-testid":`member-row-${d.id}`,children:[a("div",{children:[e("span",{style:{fontWeight:600,fontSize:"0.88rem",color:"#0f172a",marginRight:"8px"},children:d.displayName}),a("span",{style:{fontSize:"0.78rem",color:"#64748b"},children:["(",d.email,")"]})]}),e("button",{type:"button",onClick:()=>n(d.id,p),"data-testid":`toggle-member-btn-${d.id}`,style:{padding:"4px 12px",borderRadius:"4px",border:p?"1px solid #b91c1c":"1px solid #0284c7",backgroundColor:p?"#ffffff":"#0284c7",color:p?"#b91c1c":"#ffffff",fontSize:"0.78rem",fontWeight:600,cursor:"pointer"},children:p?t.groupMembersView.actionRemove:t.groupMembersView.actionAdd})]},d.id)})})]}):e("div",{style:{color:"#94a3b8",textAlign:"center",padding:"2rem"},children:t.groupMembersView.selectPrompt})})]})}function K(){let o=k(),s=V(),t=o.labels??v,[u,g]=f(""),[i,r]=f("email"),[n,d]=f(""),p=l=>{l.preventDefault(),u&&(s.submit({intent:"addAllowlistEntry",pattern:u,type:i,description:n},{method:"post"}),g(""),d(""))},x=l=>{s.submit({intent:"removeAllowlistEntry",entryId:String(l)},{method:"post"})};return a("section",{"data-testid":"allowlist-view-section",style:{display:"flex",flexDirection:"column",gap:"1.2rem"},children:[e(G,{fetcher:s}),a("form",{onSubmit:p,style:{backgroundColor:"#ffffff",padding:"1.2rem",borderRadius:"8px",border:"1px solid #e2e8f0",display:"flex",flexWrap:"wrap",gap:"0.8rem",alignItems:"flex-end"},children:[a("div",{style:{flex:2,minWidth:"220px"},children:[e("label",{htmlFor:"allowlist-pattern-input-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.pattern}),e("input",{id:"allowlist-pattern-input-field",type:"text",value:u,onChange:l=>g(l.target.value),required:!0,"data-testid":"allowlist-pattern-input",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"}})]}),a("div",{style:{flex:1,minWidth:"140px"},children:[e("label",{htmlFor:"allowlist-type-select-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.type}),a("select",{id:"allowlist-type-select-field",value:i,onChange:l=>r(l.target.value),"data-testid":"allowlist-type-select",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"},children:[e("option",{value:"email",children:t.allowlistView.emailType}),e("option",{value:"domain",children:t.allowlistView.domainType})]})]}),a("div",{style:{flex:2,minWidth:"200px"},children:[e("label",{htmlFor:"allowlist-desc-input-field",style:{display:"block",fontSize:"0.78rem",color:"#475569",marginBottom:"4px"},children:t.allowlistView.description}),e("input",{id:"allowlist-desc-input-field",type:"text",value:n,onChange:l=>d(l.target.value),"data-testid":"allowlist-desc-input",style:{width:"100%",padding:"6px 10px",borderRadius:"6px",border:"1px solid #cbd5e1",fontSize:"0.85rem",boxSizing:"border-box"}})]}),e("button",{type:"submit","data-testid":"submit-allowlist-add",style:{padding:"7px 16px",borderRadius:"6px",backgroundColor:"#047857",color:"#ffffff",border:"none",fontSize:"0.85rem",fontWeight:600,cursor:"pointer"},children:t.allowlistView.addEntry})]}),e("div",{style:{backgroundColor:"#ffffff",borderRadius:"8px",border:"1px solid #e2e8f0",overflow:"hidden"},children:a("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.85rem",textAlign:"left"},children:[e("thead",{children:a("tr",{style:{backgroundColor:"#f8fafc",borderBottom:"1px solid #e2e8f0",color:"#475569"},children:[e("th",{style:{padding:"10px 14px"},children:"ID"}),e("th",{style:{padding:"10px 14px"},children:t.allowlistView.pattern}),e("th",{style:{padding:"10px 14px"},children:t.allowlistView.type}),e("th",{style:{padding:"10px 14px"},children:t.allowlistView.description}),e("th",{style:{padding:"10px 14px",textAlign:"right"},children:t.allowlistView.actions})]})}),a("tbody",{children:[o.allowlist.map(l=>a("tr",{style:{borderBottom:"1px solid #f1f5f9"},"data-testid":`allowlist-row-${l.id}`,children:[a("td",{style:{padding:"10px 14px",color:"#64748b"},children:["#",l.id]}),e("td",{style:{padding:"10px 14px",fontWeight:600,color:"#0f172a",fontFamily:"monospace"},children:l.pattern}),e("td",{style:{padding:"10px 14px"},children:e("span",{style:{fontSize:"0.75rem",padding:"2px 8px",borderRadius:"10px",backgroundColor:l.type==="email"?"#e0f2fe":"#fef3c7",color:l.type==="email"?"#0369a1":"#b45309",fontWeight:600},children:l.type==="email"?t.allowlistView.emailTypeShort:t.allowlistView.domainTypeShort})}),e("td",{style:{padding:"10px 14px",color:"#475569"},children:l.description||"-"}),e("td",{style:{padding:"10px 14px",textAlign:"right"},children:l.readOnly?e("span",{"data-testid":`readonly-allowlist-${l.id}`,style:{fontSize:"0.75rem",color:"#94a3b8"},children:t.allowlistView.readOnly}):e("button",{type:"button",onClick:()=>x(l.id),"data-testid":`remove-allowlist-btn-${l.id}`,style:{padding:"3px 8px",borderRadius:"4px",border:"1px solid #fee2e2",backgroundColor:"#fff5f5",color:"#b91c1c",fontSize:"0.78rem",cursor:"pointer"},children:"\u524A\u9664"})})]},l.id)),o.allowlist.length===0&&e("tr",{children:e("td",{colSpan:5,style:{padding:"2rem",textAlign:"center",color:"#94a3b8"},children:t.allowlistView.empty})})]})]})})]})}var q={users:P,groups:W,group_members:$,allowlist:K};function ne({renderHeader:o}={}){let t=k()?.segment??"users",u=q[t]??P;return e(M,{renderHeader:o,children:e(u,{})})}export{K as AllowlistView,ne as AuthUserAdminAppView,$ as GroupMembersView,W as GroupsView,M as UserAdminShell,P as UsersView,Q as createInMemoryUserAdminStore,J as createUserAdminApp,v as defaultUserAdminLabels,O as jaUserAdminLabels,z as resolveUserAdminLabels};
2
+ //# sourceMappingURL=admin.mjs.map