@burdenoff/microfe-organization 2026.527.1 → 2026.527.3
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/organization-management/modules/members/components/InviteMemberForm.js +2 -0
- package/dist/organization-management/modules/members/components/InviteMemberForm.js.map +1 -1
- package/dist/organization-management/modules/members/components/MyInviteCard.js.map +1 -1
- package/dist/organization-management/modules/members/components/PendingInvitesList.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js +6 -0
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +4 -4
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +4 -0
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/EmptyState.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/ErrorState.js.map +1 -1
- package/dist/organization-management/shared/components/input/SearchInput.js.map +1 -1
- package/package.json +2 -1
|
@@ -53,6 +53,7 @@ function s({ organizationId: s, onSubmit: c, onCancel: l, loading: u }) {
|
|
|
53
53
|
...f,
|
|
54
54
|
email: e.target.value
|
|
55
55
|
}),
|
|
56
|
+
"aria-label": d("members.invite.emailLabel", "Email Address"),
|
|
56
57
|
className: "\n w-full pl-10 pr-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
57
58
|
placeholder: d("members.invite.emailPlaceholder", "user@example.com"),
|
|
58
59
|
disabled: u,
|
|
@@ -86,6 +87,7 @@ function s({ organizationId: s, onSubmit: c, onCancel: l, loading: u }) {
|
|
|
86
87
|
...f,
|
|
87
88
|
message: e.target.value
|
|
88
89
|
}),
|
|
90
|
+
"aria-label": d("members.invite.messageLabel", "Personal Message"),
|
|
89
91
|
className: "\n w-full px-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n resize-none\n ",
|
|
90
92
|
placeholder: d("members.invite.messagePlaceholder", "Add a personal message to the invite..."),
|
|
91
93
|
rows: 3,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberForm.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/InviteMemberForm.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Mail, Send } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface InviteMemberFormProps {\n organizationId: string;\n onSubmit: (data: { organizationId: string; email: string; message?: string }) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\n/**\n * Invite Member Form\n * Email-based form for inviting users to an organization.\n * Role defaults to MEMBER on the backend — admin can change it after the user joins.\n */\nexport function InviteMemberForm({\n organizationId,\n onSubmit,\n onCancel,\n loading,\n}: InviteMemberFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n email: '',\n message: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await onSubmit({\n organizationId,\n email: formData.email,\n message: formData.message || undefined,\n });\n setFormData({ email: '', message: '' });\n } catch {\n // Error is surfaced by the parent page via the mutation hook's error state\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Email */}\n <div>\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.emailLabel', 'Email Address')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none\">\n <Mail size={16} className=\"text-[var(--color-text-muted)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => setFormData({ ...formData, email: e.target.value })}\n className=\"\n w-full pl-10 pr-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n \"\n placeholder={tr('members.invite.emailPlaceholder', 'user@example.com')}\n disabled={loading}\n required\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-muted)] mt-1\">\n {tr(\n 'members.invite.emailHelp',\n 'The user will be invited as a Member. You can change their role after they join.'\n )}\n </p>\n </div>\n\n {/* Message (optional) */}\n <div>\n <label\n htmlFor=\"message\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.messageLabel', 'Personal Message')}{' '}\n <span className=\"text-[var(--color-text-muted)]\">\n ({tr('common.optional', 'optional')})\n </span>\n </label>\n <textarea\n id=\"message\"\n value={formData.message}\n onChange={(e) => setFormData({ ...formData, message: e.target.value })}\n className=\"\n w-full px-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n resize-none\n \"\n placeholder={tr(\n 'members.invite.messagePlaceholder',\n 'Add a personal message to the invite...'\n )}\n rows={3}\n maxLength={500}\n disabled={loading}\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"\n inline-flex items-center gap-2 px-6 py-2.5\n bg-[var(--color-action-primary-bg)]\n text-[var(--color-action-primary-text)]\n rounded-lg\n hover:opacity-90 hover:scale-[1.02]\n active:scale-[0.98]\n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[180px]\n \"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : <Send size={16} />}\n {tr('members.invite.send', 'Send Invite')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"\n px-6 py-2.5\n bg-transparent\n text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-bg-subtle)]\n active:scale-[0.98]\n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[140px]\n \"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgB,EAAiB,EAC/B,mBACA,aACA,aACA,cACwB;CACxB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAS;EACvC,OAAO;EACP,SAAS;EACV,CAAC;AAgBF,QACE,kBAAC,QAAD;EAAM,UAfa,OAAO,MAAuB;AACjD,KAAE,gBAAgB;AAClB,OAAI;AAMF,IALA,MAAM,EAAS;KACb;KACA,OAAO,EAAS;KAChB,SAAS,EAAS,WAAW,KAAA;KAC9B,CAAC,EACF,EAAY;KAAE,OAAO;KAAI,SAAS;KAAI,CAAC;WACjC;;EAMsB,WAAU;YAAxC;GAEE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,SAAD;KACE,SAAQ;KACR,WAAU;eAFZ;MAIG,EAAG,6BAA6B,gBAAgB;MAAE;MACnD,kBAAC,QAAD;OAAM,WAAU;iBAAwC;OAAQ,CAAA;MAC1D;;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAM,MAAM;OAAI,WAAU;OAAmC,CAAA;MACzD,CAAA,EACN,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO,EAAS;MAChB,WAAW,MAAM,EAAY;OAAE,GAAG;OAAU,OAAO,EAAE,OAAO;OAAO,CAAC;MACpE,WAAU;MAUV,aAAa,EAAG,mCAAmC,mBAAmB;MACtE,UAAU;MACV,UAAA;MACA,CAAA,CACE;;IACN,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,4BACA,mFACD;KACC,CAAA;IACA,EAAA,CAAA;GAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IACE,SAAQ;IACR,WAAU;cAFZ;KAIG,EAAG,+BAA+B,mBAAmB;KAAE;KACxD,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAAiD;OAC7C,EAAG,mBAAmB,WAAW;OAAC;OAC/B;;KACD;OACR,kBAAC,YAAD;IACE,IAAG;IACH,OAAO,EAAS;IAChB,WAAW,MAAM,EAAY;KAAE,GAAG;KAAU,SAAS,EAAE,OAAO;KAAO,CAAC;IACtE,WAAU;IAWV,aAAa,EACX,qCACA,0CACD;IACD,MAAM;IACN,WAAW;IACX,UAAU;IACV,CAAA,CACE,EAAA,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,WAAU;eAHZ,CAgBG,IAAU,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAC7E,EAAG,uBAAuB,cAAc,CAClC;QAET,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAcT,EAAG,iBAAiB,SAAS;KACvB,CAAA,CACL;;GACD"}
|
|
1
|
+
{"version":3,"file":"InviteMemberForm.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/InviteMemberForm.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Mail, Send } from 'lucide-react';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface InviteMemberFormProps {\n organizationId: string;\n onSubmit: (data: { organizationId: string; email: string; message?: string }) => Promise<void>;\n onCancel: () => void;\n loading?: boolean;\n}\n\n/**\n * Invite Member Form\n * Email-based form for inviting users to an organization.\n * Role defaults to MEMBER on the backend — admin can change it after the user joins.\n */\nexport function InviteMemberForm({\n organizationId,\n onSubmit,\n onCancel,\n loading,\n}: InviteMemberFormProps) {\n const tr = useTr();\n const [formData, setFormData] = useState({\n email: '',\n message: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n await onSubmit({\n organizationId,\n email: formData.email,\n message: formData.message || undefined,\n });\n setFormData({ email: '', message: '' });\n } catch {\n // Error is surfaced by the parent page via the mutation hook's error state\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {/* Email */}\n <div>\n <label\n htmlFor=\"email\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.emailLabel', 'Email Address')}{' '}\n <span className=\"text-[var(--color-status-error-text)]\">*</span>\n </label>\n <div className=\"relative\">\n <div className=\"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none\">\n <Mail size={16} className=\"text-[var(--color-text-muted)]\" />\n </div>\n <input\n id=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => setFormData({ ...formData, email: e.target.value })}\n aria-label={tr('members.invite.emailLabel', 'Email Address')}\n className=\"\n w-full pl-10 pr-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n \"\n placeholder={tr('members.invite.emailPlaceholder', 'user@example.com')}\n disabled={loading}\n required\n />\n </div>\n <p className=\"text-xs text-[var(--color-text-muted)] mt-1\">\n {tr(\n 'members.invite.emailHelp',\n 'The user will be invited as a Member. You can change their role after they join.'\n )}\n </p>\n </div>\n\n {/* Message (optional) */}\n <div>\n <label\n htmlFor=\"message\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-1\"\n >\n {tr('members.invite.messageLabel', 'Personal Message')}{' '}\n <span className=\"text-[var(--color-text-muted)]\">\n ({tr('common.optional', 'optional')})\n </span>\n </label>\n <textarea\n id=\"message\"\n value={formData.message}\n onChange={(e) => setFormData({ ...formData, message: e.target.value })}\n aria-label={tr('members.invite.messageLabel', 'Personal Message')}\n className=\"\n w-full px-4 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n resize-none\n \"\n placeholder={tr(\n 'members.invite.messagePlaceholder',\n 'Add a personal message to the invite...'\n )}\n rows={3}\n maxLength={500}\n disabled={loading}\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-3 border-t border-[var(--color-border-default)] pt-4 sm:flex-row\">\n <button\n type=\"submit\"\n disabled={loading}\n className=\"\n inline-flex items-center gap-2 px-6 py-2.5\n bg-[var(--color-action-primary-bg)]\n text-[var(--color-action-primary-text)]\n rounded-lg\n hover:opacity-90 hover:scale-[1.02]\n active:scale-[0.98]\n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[180px]\n \"\n >\n {loading ? <Loader2 size={16} className=\"animate-spin\" /> : <Send size={16} />}\n {tr('members.invite.send', 'Send Invite')}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={loading}\n className=\"\n px-6 py-2.5\n bg-transparent\n text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-bg-subtle)]\n active:scale-[0.98]\n transition-all duration-200\n font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n sm:min-w-[140px]\n \"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgB,EAAiB,EAC/B,mBACA,aACA,aACA,cACwB;CACxB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAS;EACvC,OAAO;EACP,SAAS;EACV,CAAC;AAgBF,QACE,kBAAC,QAAD;EAAM,UAfa,OAAO,MAAuB;AACjD,KAAE,gBAAgB;AAClB,OAAI;AAMF,IALA,MAAM,EAAS;KACb;KACA,OAAO,EAAS;KAChB,SAAS,EAAS,WAAW,KAAA;KAC9B,CAAC,EACF,EAAY;KAAE,OAAO;KAAI,SAAS;KAAI,CAAC;WACjC;;EAMsB,WAAU;YAAxC;GAEE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,SAAD;KACE,SAAQ;KACR,WAAU;eAFZ;MAIG,EAAG,6BAA6B,gBAAgB;MAAE;MACnD,kBAAC,QAAD;OAAM,WAAU;iBAAwC;OAAQ,CAAA;MAC1D;;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAM,MAAM;OAAI,WAAU;OAAmC,CAAA;MACzD,CAAA,EACN,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO,EAAS;MAChB,WAAW,MAAM,EAAY;OAAE,GAAG;OAAU,OAAO,EAAE,OAAO;OAAO,CAAC;MACpE,cAAY,EAAG,6BAA6B,gBAAgB;MAC5D,WAAU;MAUV,aAAa,EAAG,mCAAmC,mBAAmB;MACtE,UAAU;MACV,UAAA;MACA,CAAA,CACE;;IACN,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,4BACA,mFACD;KACC,CAAA;IACA,EAAA,CAAA;GAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IACE,SAAQ;IACR,WAAU;cAFZ;KAIG,EAAG,+BAA+B,mBAAmB;KAAE;KACxD,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAAiD;OAC7C,EAAG,mBAAmB,WAAW;OAAC;OAC/B;;KACD;OACR,kBAAC,YAAD;IACE,IAAG;IACH,OAAO,EAAS;IAChB,WAAW,MAAM,EAAY;KAAE,GAAG;KAAU,SAAS,EAAE,OAAO;KAAO,CAAC;IACtE,cAAY,EAAG,+BAA+B,mBAAmB;IACjE,WAAU;IAWV,aAAa,EACX,qCACA,0CACD;IACD,MAAM;IACN,WAAW;IACX,UAAU;IACV,CAAA,CACE,EAAA,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,WAAU;eAHZ,CAgBG,IAAU,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAC7E,EAAG,uBAAuB,cAAc,CAClC;QAET,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAcT,EAAG,iBAAiB,SAAS;KACvB,CAAA,CACL;;GACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyInviteCard.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/MyInviteCard.tsx"],"sourcesContent":["import { Check, X, Building2, Clock, Loader2 } from 'lucide-react';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface MyInviteCardProps {\n invite: {\n id: string;\n email: string;\n role: string;\n expiresAt: string;\n message?: string | null;\n createdAt: string;\n organization?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n };\n onAccept: (inviteId: string) => Promise<void>;\n onReject: (inviteId: string) => Promise<void>;\n accepting?: boolean;\n rejecting?: boolean;\n}\n\nexport function MyInviteCard({\n invite,\n onAccept,\n onReject,\n accepting,\n rejecting,\n}: MyInviteCardProps) {\n const tr = useTr();\n const daysUntilExpiry = Math.max(\n 0,\n Math.ceil((new Date(invite.expiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))\n );\n\n return (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 transition-colors hover:border-[var(--color-border-focus)]\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n {/* Left: Org info */}\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"p-2 rounded-lg bg-[var(--color-bg-subtle)]\">\n <Building2 size={20} className=\"text-[var(--color-text-secondary)]\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] truncate\">\n {invite.organization?.name ?? tr('common.organization', 'Organization')}\n </h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <MemberRoleBadge role={invite.role} />\n <span className=\"text-xs text-[var(--color-text-muted)] flex items-center gap-1\">\n <Clock size={12} />\n {daysUntilExpiry > 0\n ? `${tr('members.myInvites.expiresIn', 'Expires in')} ${daysUntilExpiry}d`\n : tr('members.myInvites.expiringSoon', 'Expiring soon')}\n </span>\n </div>\n {invite.message && (\n <p className=\"mt-2 text-sm text-[var(--color-text-secondary)] italic\">\n “{invite.message}”\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Actions */}\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button
|
|
1
|
+
{"version":3,"file":"MyInviteCard.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/MyInviteCard.tsx"],"sourcesContent":["import { Check, X, Building2, Clock, Loader2 } from 'lucide-react';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface MyInviteCardProps {\n invite: {\n id: string;\n email: string;\n role: string;\n expiresAt: string;\n message?: string | null;\n createdAt: string;\n organization?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n };\n onAccept: (inviteId: string) => Promise<void>;\n onReject: (inviteId: string) => Promise<void>;\n accepting?: boolean;\n rejecting?: boolean;\n}\n\nexport function MyInviteCard({\n invite,\n onAccept,\n onReject,\n accepting,\n rejecting,\n}: MyInviteCardProps) {\n const tr = useTr();\n const daysUntilExpiry = Math.max(\n 0,\n Math.ceil((new Date(invite.expiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24))\n );\n\n return (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 transition-colors hover:border-[var(--color-border-focus)]\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n {/* Left: Org info */}\n <div className=\"flex items-start gap-3 min-w-0\">\n <div className=\"p-2 rounded-lg bg-[var(--color-bg-subtle)]\">\n <Building2 size={20} className=\"text-[var(--color-text-secondary)]\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-[var(--color-text-primary)] truncate\">\n {invite.organization?.name ?? tr('common.organization', 'Organization')}\n </h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <MemberRoleBadge role={invite.role} />\n <span className=\"text-xs text-[var(--color-text-muted)] flex items-center gap-1\">\n <Clock size={12} />\n {daysUntilExpiry > 0\n ? `${tr('members.myInvites.expiresIn', 'Expires in')} ${daysUntilExpiry}d`\n : tr('members.myInvites.expiringSoon', 'Expiring soon')}\n </span>\n </div>\n {invite.message && (\n <p className=\"mt-2 text-sm text-[var(--color-text-secondary)] italic\">\n “{invite.message}”\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Actions */}\n <div className=\"flex flex-wrap items-center gap-2 shrink-0\">\n <button\n type=\"button\"\n onClick={() => onAccept(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-[var(--color-status-success-bg)] text-[var(--color-text-inverse)] rounded-lg\n hover:opacity-90\n active:scale-[0.98]\n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {accepting ? <Loader2 size={14} className=\"animate-spin\" /> : <Check size={14} />}\n {tr('members.myInvites.accept', 'Accept')}\n </button>\n <button\n type=\"button\"\n onClick={() => onReject(invite.id)}\n disabled={accepting || rejecting}\n className=\"\n inline-flex items-center gap-1.5 px-4 py-2\n bg-transparent text-[var(--color-text-primary)]\n border border-[var(--color-border-default)]\n rounded-lg\n hover:bg-[var(--color-status-error-bgSubtle)] hover:border-[var(--color-status-error-border)] hover:text-[var(--color-status-error-text)]\n active:scale-[0.98]\n transition-all duration-200\n text-sm font-medium\n disabled:opacity-50 disabled:cursor-not-allowed\n \"\n >\n {rejecting ? <Loader2 size={14} className=\"animate-spin\" /> : <X size={14} />}\n {tr('members.myInvites.decline', 'Decline')}\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAwBA,SAAgB,EAAa,EAC3B,WACA,aACA,aACA,cACA,gBACoB;CACpB,IAAM,IAAK,GAAO,EACZ,IAAkB,KAAK,IAC3B,GACA,KAAK,MAAM,IAAI,KAAK,EAAO,UAAU,CAAC,SAAS,GAAG,KAAK,KAAK,KAAK,MAAO,KAAK,KAAK,IAAI,CACvF;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAW,MAAM;MAAI,WAAU;MAAuC,CAAA;KAClE,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAO,cAAc,QAAQ,EAAG,uBAAuB,eAAe;OACpE,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA,EACtC,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAClB,IAAkB,IACf,GAAG,EAAG,+BAA+B,aAAa,CAAC,GAAG,EAAgB,KACtE,EAAG,kCAAkC,gBAAgB,CACpD;UACH;;MACL,EAAO,WACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAsE;QAC5D,EAAO;QAAQ;QACrB;;MAEF;OACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,EAAO,GAAG;KAClC,UAAU,KAAa;KACvB,WAAU;eAJZ,CAcG,IAAY,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAChF,EAAG,4BAA4B,SAAS,CAClC;QACT,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,EAAO,GAAG;KAClC,UAAU,KAAa;KACvB,WAAU;eAJZ,CAgBG,IAAY,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAAG,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA,EAC5E,EAAG,6BAA6B,UAAU,CACpC;OACL;MACF;;EACF,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingInvitesList.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/PendingInvitesList.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { RotateCw, X, Loader2, AlertCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface PendingInvitesListProps {\n organizationId: string;\n}\n\nexport function PendingInvitesList({ organizationId }: PendingInvitesListProps) {\n const tr = useTr();\n const { invites, loading, error, total } = useOrganizationInvites(\n organizationId,\n { status: InviteStatus.Pending },\n 50\n );\n const { cancelInvite, loading: cancelling } = useCancelInvite();\n const { resendInvite, loading: resending } = useResendInvite();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (loading && invites.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 size={20} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <p className=\"py-6 text-center text-sm text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n );\n }\n\n if (invites.length === 0) {\n return (\n <p className=\"text-sm text-[var(--color-text-muted)] text-center py-6\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </p>\n );\n }\n\n const handleCancel = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await cancelInvite(inviteId);\n toast.success(`${tr('members.invite.cancelSuccess', 'Invite cancelled for')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.cancelFailed', 'Failed to cancel invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const handleResend = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await resendInvite(inviteId);\n toast.success(`${tr('members.invite.resendSuccess', 'Invite re-sent to')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.resendFailed', 'Failed to resend invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n return (\n <div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-secondary)] uppercase tracking-wider mb-3\">\n {tr('members.invite.pendingTitle', 'Pending Invites')} ({total})\n </h3>\n\n {actionError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-2.5\">\n <AlertCircle size={14} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{actionError}</p>\n </div>\n )}\n\n <div className=\"overflow-x-auto rounded-lg border border-[var(--color-border-default)]\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-[var(--color-bg-subtle)] border-b border-[var(--color-border-default)]\">\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.emailLabel', 'Email Address')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.expires', 'Expires')}\n </th>\n <th className=\"text-right px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {invites.map((invite) => (\n <tr\n key={invite.id}\n className=\"border-b border-[var(--color-border-default)] last:border-b-0 hover:bg-[var(--color-bg-subtle)] transition-colors\"\n >\n <td className=\"px-4 py-3 text-[var(--color-text-primary)]\">{invite.email}</td>\n <td className=\"px-4 py-3\">\n <MemberRoleBadge role={invite.role} />\n </td>\n <td className=\"px-4 py-3\">\n <InviteStatusBadge status={invite.status} />\n </td>\n <td className=\"px-4 py-3 text-[var(--color-text-muted)]\">\n {new Date(invite.expiresAt).toLocaleDateString()}\n </td>\n <td className=\"px-4 py-3 text-right\">\n <div className=\"flex items-center justify-end gap-1\">\n <button
|
|
1
|
+
{"version":3,"file":"PendingInvitesList.js","names":[],"sources":["../../../../../src/organization-management/modules/members/components/PendingInvitesList.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { RotateCw, X, Loader2, AlertCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useOrganizationInvites, useCancelInvite, useResendInvite } from '../hooks/useInvites';\nimport { InviteStatusBadge } from './InviteStatusBadge';\nimport { MemberRoleBadge } from './MemberRoleBadge';\nimport { InviteStatus } from '../../../../generated/global-types';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface PendingInvitesListProps {\n organizationId: string;\n}\n\nexport function PendingInvitesList({ organizationId }: PendingInvitesListProps) {\n const tr = useTr();\n const { invites, loading, error, total } = useOrganizationInvites(\n organizationId,\n { status: InviteStatus.Pending },\n 50\n );\n const { cancelInvite, loading: cancelling } = useCancelInvite();\n const { resendInvite, loading: resending } = useResendInvite();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (loading && invites.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 size={20} className=\"animate-spin text-[var(--color-text-muted)]\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <p className=\"py-6 text-center text-sm text-[var(--color-status-error-text)]\">\n {tr('members.myInvites.loadFailed', 'Failed to load invites')}: {error.message}\n </p>\n );\n }\n\n if (invites.length === 0) {\n return (\n <p className=\"text-sm text-[var(--color-text-muted)] text-center py-6\">\n {tr('members.myInvites.emptyTitle', 'No pending invites')}\n </p>\n );\n }\n\n const handleCancel = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await cancelInvite(inviteId);\n toast.success(`${tr('members.invite.cancelSuccess', 'Invite cancelled for')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.cancelFailed', 'Failed to cancel invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n const handleResend = async (inviteId: string, email: string) => {\n try {\n setActionError(null);\n await resendInvite(inviteId);\n toast.success(`${tr('members.invite.resendSuccess', 'Invite re-sent to')} ${email}`);\n } catch (err) {\n const msg =\n err instanceof Error\n ? err.message\n : tr('members.invite.resendFailed', 'Failed to resend invite');\n setActionError(msg);\n toast.error(msg);\n }\n };\n\n return (\n <div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-secondary)] uppercase tracking-wider mb-3\">\n {tr('members.invite.pendingTitle', 'Pending Invites')} ({total})\n </h3>\n\n {actionError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-2.5\">\n <AlertCircle size={14} className=\"shrink-0 text-[var(--color-status-error-text)]\" />\n <p className=\"text-sm text-[var(--color-status-error-text)]\">{actionError}</p>\n </div>\n )}\n\n <div className=\"overflow-x-auto rounded-lg border border-[var(--color-border-default)]\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-[var(--color-bg-subtle)] border-b border-[var(--color-border-default)]\">\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.emailLabel', 'Email Address')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </th>\n <th className=\"text-left px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('members.invite.expires', 'Expires')}\n </th>\n <th className=\"text-right px-4 py-2.5 font-medium text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {invites.map((invite) => (\n <tr\n key={invite.id}\n className=\"border-b border-[var(--color-border-default)] last:border-b-0 hover:bg-[var(--color-bg-subtle)] transition-colors\"\n >\n <td className=\"px-4 py-3 text-[var(--color-text-primary)]\">{invite.email}</td>\n <td className=\"px-4 py-3\">\n <MemberRoleBadge role={invite.role} />\n </td>\n <td className=\"px-4 py-3\">\n <InviteStatusBadge status={invite.status} />\n </td>\n <td className=\"px-4 py-3 text-[var(--color-text-muted)]\">\n {new Date(invite.expiresAt).toLocaleDateString()}\n </td>\n <td className=\"px-4 py-3 text-right\">\n <div className=\"flex items-center justify-end gap-1\">\n <button\n type=\"button\"\n onClick={() => handleResend(invite.id, invite.email)}\n disabled={resending}\n className=\"p-1.5 rounded-md hover:bg-[var(--color-bg-subtle)] text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] transition-colors\"\n title={tr('members.invite.resend', 'Resend invite')}\n aria-label={`${tr('members.invite.resendFor', 'Resend invite for')} ${invite.email}`}\n >\n <RotateCw size={14} />\n </button>\n <button\n type=\"button\"\n onClick={() => handleCancel(invite.id, invite.email)}\n disabled={cancelling}\n className=\"p-1.5 rounded-md text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-status-error-bgSubtle)] hover:text-[var(--color-status-error-text)]\"\n title={tr('members.invite.cancel', 'Cancel invite')}\n aria-label={`${tr('members.invite.cancelFor', 'Cancel invite for')} ${invite.email}`}\n >\n <X size={14} />\n </button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useInviteMember } from '../hooks/useInvites';\nimport { InviteMemberForm } from '../components/InviteMemberForm';\nimport { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Invite Member Page\n * Email-based invite form with pending invites list below\n */\nexport function InviteMemberPage() {\n const tr = useTr();\n const { organizationId } = useParams<{ organizationId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { inviteMember, loading, error } = useInviteMember();\n\n const handleSubmit = async (data: {\n organizationId: string;\n email: string;\n message?: string;\n }) => {\n await inviteMember(data);\n toast.success(`${tr('members.invite.success', 'Invitation sent to')} ${data.email}`);\n };\n\n const handleCancel = () => {\n navigate(`${basePath}/${organizationId}/members`);\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_INVITE)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.invite.noPermission', \"You don't have permission to invite members.\")}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"mx-auto max-w-3xl space-y-8\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <button
|
|
1
|
+
{"version":3,"file":"InviteMemberPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InviteMemberPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, UserPlus } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useInviteMember } from '../hooks/useInvites';\nimport { InviteMemberForm } from '../components/InviteMemberForm';\nimport { PendingInvitesList } from '../components/PendingInvitesList';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Invite Member Page\n * Email-based invite form with pending invites list below\n */\nexport function InviteMemberPage() {\n const tr = useTr();\n const { organizationId } = useParams<{ organizationId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { inviteMember, loading, error } = useInviteMember();\n\n const handleSubmit = async (data: {\n organizationId: string;\n email: string;\n message?: string;\n }) => {\n await inviteMember(data);\n toast.success(`${tr('members.invite.success', 'Invitation sent to')} ${data.email}`);\n };\n\n const handleCancel = () => {\n navigate(`${basePath}/${organizationId}/members`);\n };\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_INVITE)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.invite.noPermission', \"You don't have permission to invite members.\")}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <div className=\"mx-auto max-w-3xl space-y-8\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"rounded-lg p-2 transition-colors hover:bg-[var(--color-bg-subtle)]\"\n aria-label={tr('members.invite.backToMembers', 'Back to members')}\n >\n <ArrowLeft size={20} className=\"text-[var(--color-text-secondary)]\" />\n </button>\n <div className=\"flex items-center gap-2\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" />\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invite.title', 'Invite Member')}\n </h1>\n </div>\n </div>\n\n {/* Invite Form Card */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.invite.send', 'Send Invite')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-muted)] mb-6\">\n {tr(\n 'members.invite.description',\n 'Enter the email address of the person you want to invite. They will receive an email notification and can accept or decline from their account.'\n )}\n </p>\n\n {error && (\n <div className=\"mb-4 rounded-lg border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] p-3\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error.message.includes('CONFLICT')\n ? tr(\n 'members.invite.error.conflict',\n 'An invite already exists for this email address.'\n )\n : error.message.includes('already a member')\n ? tr(\n 'members.invite.error.alreadyMember',\n 'This user is already a member of the organization.'\n )\n : error.message}\n </p>\n </div>\n )}\n\n <InviteMemberForm\n organizationId={organizationId!}\n onSubmit={handleSubmit}\n onCancel={handleCancel}\n loading={loading}\n />\n </div>\n\n {/* Pending Invites */}\n <div className=\"bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)] p-6\">\n <PendingInvitesList organizationId={organizationId!} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,SAAgB,IAAmB;CACjC,IAAM,IAAK,GAAO,EACZ,EAAE,sBAAmB,GAAuC,EAC5D,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,iBAAc,YAAS,aAAU,GAAiB,EAEpD,IAAe,OAAO,MAItB;AAEJ,EADA,MAAM,EAAa,EAAK,EACxB,EAAM,QAAQ,GAAG,EAAG,0BAA0B,qBAAqB,CAAC,GAAG,EAAK,QAAQ;IAGhF,UAAqB;AACzB,IAAS,GAAG,EAAS,GAAG,EAAe,UAAU;;AAqBnD,QAlBI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIL,EAAc,EAAY,cAAc,GAW3C,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,cAAY,EAAG,gCAAgC,kBAAkB;gBAEjE,kBAAC,GAAD;OAAW,MAAM;OAAI,WAAU;OAAuC,CAAA;MAC/D,CAAA,EACT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAU,MAAM;OAAI,WAAU;OAAqC,CAAA,EACnE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,wBAAwB,gBAAgB;OACzC,CAAA,CACD;QACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,uBAAuB,cAAc;OACtC,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,8BACA,kJACD;OACC,CAAA;MAEH,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,QAAQ,SAAS,WAAW,GAC/B,EACE,iCACA,mDACD,GACD,EAAM,QAAQ,SAAS,mBAAmB,GACxC,EACE,sCACA,qDACD,GACD,EAAM;QACV,CAAA;OACA,CAAA;MAGR,kBAAC,GAAD;OACkB;OAChB,UAAU;OACV,UAAU;OACD;OACT,CAAA;MACE;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAoC,mBAAmB,CAAA;KACnD,CAAA;IACF;;EACF,CAAA,GAxEJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,+BAA+B,+CAA+C;GAC9E,CAAA;EACF,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Member Detail Page\n * Simplified version showing available member fields\n */\nexport function MemberDetailPage() {\n const tr = useTr();\n const { organizationId, userId } = useParams<{ organizationId: string; userId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { member, loading, error } = useMember(organizationId!, userId!);\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.detail.noPermission', \"You don't have permission to view member details.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.detail.loading', 'Loading member...')} />\n </div>\n );\n }\n\n if (error || !member) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('members.detail.notFound', 'Member not found')}\n action={{\n label: tr('common.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/${organizationId}/members`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n {/* Header */}\n <div className=\"mb-6\">\n <button
|
|
1
|
+
{"version":3,"file":"MemberDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MemberDetailPage.tsx"],"sourcesContent":["import { useParams } from 'react-router-dom';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Member Detail Page\n * Simplified version showing available member fields\n */\nexport function MemberDetailPage() {\n const tr = useTr();\n const { organizationId, userId } = useParams<{ organizationId: string; userId: string }>();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { member, loading, error } = useMember(organizationId!, userId!);\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.detail.noPermission', \"You don't have permission to view member details.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.detail.loading', 'Loading member...')} />\n </div>\n );\n }\n\n if (error || !member) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('members.detail.notFound', 'Member not found')}\n action={{\n label: tr('common.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/${organizationId}/members`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n {/* Header */}\n <div className=\"mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members`)}\n className=\"inline-flex items-center gap-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] active:scale-[0.98] transition-all duration-200 mb-4\"\n >\n <ArrowLeft size={20} />\n {tr('members.detail.backToMembers', 'Back to Members')}\n </button>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-full bg-[var(--color-status-info-muted)] flex items-center justify-center\">\n <Shield size={32} className=\"text-[var(--color-status-info)]\" />\n </div>\n <div>\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">{member.userId}</h1>\n <p className=\"text-[var(--color-text-secondary)] mt-1\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </div>\n </div>\n </div>\n\n {/* Details */}\n <div className=\"bg-[var(--color-bg-surface)] border border-[var(--color-border-default)] rounded-lg p-6\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)] mb-4\">\n {tr('members.detail.info', 'Member Information')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.userId', 'User ID')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.userId}</p>\n </div>\n\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.orgId', 'Organization ID')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.organizationId}</p>\n </div>\n\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </p>\n <div className=\"mt-1\">\n <MemberRoleBadge role={member.role} />\n </div>\n </div>\n\n {member.invitedBy && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.invitedBy', 'Invited By')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">{member.invitedBy}</p>\n </div>\n )}\n\n {member.joinedAt && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr('members.detail.joinedAt', 'Joined At')}\n </p>\n <p className=\"text-[var(--color-text-primary)] font-medium\">\n {new Date(member.joinedAt).toLocaleString()}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAeA,SAAgB,IAAmB;CACjC,IAAM,IAAK,GAAO,EACZ,EAAE,mBAAgB,cAAW,GAAuD,EACpF,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,WAAQ,YAAS,aAAU,EAAU,GAAiB,EAAQ;AA0CtE,QAxCI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIL,EAAc,EAAY,YAAY,GAUvC,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,0BAA0B,oBAAoB,EAAI,CAAA;EACxE,CAAA,GAIN,KAAS,CAAC,IAEV,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,SAAS,GAAO,WAAW,EAAG,2BAA2B,mBAAmB;GAC5E,QAAQ;IACN,OAAO,EAAG,iBAAiB,UAAU;IACrC,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,UAAU;IACjE;GACD,CAAA;EACE,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,UAAU;IAChE,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,gCAAgC,kBAAkB,CAC/C;OAET,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAQ,MAAM;MAAI,WAAU;MAAoC,CAAA;KAC5D,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAuD,EAAO;KAAY,CAAA,EACxF,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAG,yBAAyB,YAAY;MAAC;MAAG,EAAO;MAClD;OACA,EAAA,CAAA,CACF;MACF;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,uBAAuB,qBAAqB;IAC7C,CAAA,EAEL,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,yBAAyB,UAAU;MACrC,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBAAgD,EAAO;MAAW,CAAA,CAC3E,EAAA,CAAA;KAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,wBAAwB,kBAAkB;MAC5C,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBAAgD,EAAO;MAAmB,CAAA,CACnF,EAAA,CAAA;KAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,4BAA4B,OAAO;MACrC,CAAA,EACJ,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA;MAClC,CAAA,CACF,EAAA,CAAA;KAEL,EAAO,aACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,4BAA4B,aAAa;MAC3C,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBAAgD,EAAO;MAAc,CAAA,CAC9E,EAAA,CAAA;KAGP,EAAO,YACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,2BAA2B,YAAY;MACzC,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBACV,IAAI,KAAK,EAAO,SAAS,CAAC,gBAAgB;MACzC,CAAA,CACA,EAAA,CAAA;KAEJ;MACF;KACF;MA3GJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,+BAA+B,oDAAoD;GACnF,CAAA;EACF,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Eye, Mail, RefreshCw, Shield, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter(\n (member) =>\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n }, [debouncedSearchQuery, members]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.list.title', 'Members')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'members.list.subtitle',\n 'Manage organization members, inspect their roles, and invite more people into this organization.'\n )}\n </p>\n </div>\n\n {inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members/invite`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">{tr('members.invite.button', 'Invite Member')}</span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('members.list.searchPlaceholder', 'Search by member ID or role...')}\n label={tr('members.list.searchLabel', 'Search organization members')}\n inputId=\"organization-members-search\"\n className=\"flex-1\"\n />\n\n <button type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('members.list.refresh', 'Refresh members')}\n title={tr('members.list.refresh', 'Refresh members')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {filteredMembers.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedMemberIds.size > 0 ? (\n <button type=\"button\"\n onClick={() => setSelectedMemberIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedMemberIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button type=\"button\"\n onClick={handleBulkRemove}\n disabled={selectedMembers.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.removeSelected', 'Remove Selected')}</span>\n {selectedMembers.length > 0 ? <span>({selectedMembers.length})</span> : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {filteredMembers.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Shield size={40} aria-hidden=\"true\" />}\n title={tr('members.list.emptyTitle', 'No members found')}\n description={tr(\n 'members.list.emptyDescription',\n 'Invite someone to this organization to grant them access and assign their role.'\n )}\n action={\n inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? {\n label: tr('members.invite.button', 'Invite Member'),\n onClick: () => navigate(`${basePath}/${organizationId}/members/invite`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"break-all text-base font-semibold text-[var(--color-text-primary)]\">\n {member.userId}\n </h2>\n <MemberRoleBadge role={member.role} />\n </div>\n <p className=\"mt-1 break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-primary)]\">\n {formatDate(member.joinedAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('members.remove.confirm', 'Remove')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.user', 'User')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"break-all font-medium text-[var(--color-text-primary)]\">\n {member.userId}\n </p>\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAS,IAAgB;CACvB,IAAM,IAAK,GAAO;AAClB,SAAQ,MACD,IACE,IAAI,KAAK,EAAK,CAAC,oBAAoB,GADxB,EAAG,yBAAyB,UAAU;;AAK5D,SAAgB,IAAkB;CAChC,IAAM,IAAK,GAAO,EACZ,IAAa,GAAe,EAC5B,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,WAAW,MAAuB,IAAgB,EAGnE,IAAgB,EAAsB,sBAAsB,EAC5D,IAAgB,IAAgB,EAAQ,EAAc,QAAS,IAC/D,EAAE,sBAAmB,GAAuC,EAC5D,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,IAAuB,EAAkB,GAAa,IAAI,EAC1D,CAAC,GAAmB,KAAwB,kBAAsB,IAAI,KAAK,CAAC,EAC5E,EAAE,sBAAmB,qBAAkB,IAAkB,EAEzD,EAAE,YAAS,YAAS,UAAO,eAAY,EAAW,KAAkB,IAAI,IAAI,EAAE,EAC9E,EAAE,iBAAc,SAAS,MAAkB,GAAiB,EAE5D,IAAkB,QAAc;AACpC,MAAI,CAAC,EACH,QAAO;EAGT,IAAM,IAAkB,EAAqB,aAAa;AAC1D,SAAO,EAAQ,QACZ,MACC,EAAO,OAAO,aAAa,CAAC,SAAS,EAAgB,IACrD,EAAO,KAAK,aAAa,CAAC,SAAS,EAAgB,CACtD;IACA,CAAC,GAAsB,EAAQ,CAAC;AAEnC,UAAgB;AACd,KAAsB,MAAsB;GAC1C,IAAM,IAAa,IAAI,IAAI,EAAgB,KAAK,MAAW,EAAO,GAAG,CAAC,EAChE,IAAgB,IAAI,IACxB,CAAC,GAAG,EAAkB,CAAC,QAAQ,MAAa,EAAW,IAAI,EAAS,CAAC,CACtE;AAMD,UAJI,EAAc,SAAS,EAAkB,OACpC,IAGF;IACP;IACD,CAAC,EAAgB,CAAC;CAErB,IAAM,IAAkB,QAChB,EAAgB,QAAQ,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC1E,CAAC,GAAiB,EAAkB,CACrC,EAEK,IACJ,EAAgB,SAAS,KACzB,EAAgB,OAAO,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,EAC/D,IACJ,EAAgB,MAAM,MAAW,EAAkB,IAAI,EAAO,GAAG,CAAC,IAAI,CAAC,GAEnE,IAAuB,OAAO,MAAsB;AACnD,QAEL,KAAI;GAUF,IAAM,KATU,MAAM,QAAQ,WAC5B,EAAQ,IAAI,OAAO,MAAW;IAC5B,IAAM,IAAS,MAAM,EAAa,GAAgB,EAAO;AACzD,QAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,0BAA0B;KAEzE,CACH,EAE8B,MAAM,MAAW,EAAO,WAAW,WAAW;AAC7E,OAAI,GAAgB,WAAW,WAC7B,OAAM,EAAe;AAKvB,GAFA,kBAAqB,IAAI,KAAK,CAAC,EAC/B,MAAM,GAAS,EACf,EAAM,QACJ,EAAQ,WAAW,IACf,EAAG,gCAAgC,8BAA8B,GACjE,GAAG,EAAQ,OAAO,GAAG,EAAG,8BAA8B,+BAA+B,GAC1F;WACM,GAAa;AACpB,KAAM,MACJ,aAAuB,QACnB,EAAY,UACZ,EAAG,yBAAyB,0BAA0B,CAC3D;;IAIC,KAAgB,MAAmB;AACvC,IAAkB;GAChB,OAAO,EAAG,wBAAwB,gBAAgB;GAClD,aAAa,GAAG,EAAG,oCAAoC,SAAS,CAAC,GAAG,EAAO,GAAG,EAAG,oCAAoC,4FAA4F;GACjN,aAAa,EAAG,0BAA0B,SAAS;GACnD,SAAS;GACT,iBAAiB;AACV,MAAqB,CAAC,EAAO,CAAC;;GAEtC,CAAC;IAGE,WAAyB;AACzB,IAAgB,WAAW,KAE/B,EAAkB;GAChB,OAAO,EAAG,4BAA4B,0BAA0B;GAChE,aAAa,GAAG,EAAG,6BAA6B,SAAS,CAAC,GAAG,EAAgB,OAAO,GAAG,EACrF,EAAgB,WAAW,IACvB,oCACA,iCACJ,EAAgB,WAAW,IACvB,4CACA,2CACL;GACD,aAAa,GAAG,EAAG,0BAA0B,SAAS,CAAC,GAAG,EAAgB;GAC1E,SAAS;GACT,iBAAiB;AACV,MAAqB,EAAgB,KAAK,MAAW,EAAO,OAAO,CAAC;;GAE5E,CAAC;IAGE,KAAyB,GAAkB,MAAqB;AACpE,KAAsB,MAAsB;GAC1C,IAAM,IAAgB,IAAI,IAAI,EAAkB;AAMhD,UALI,IACF,EAAc,IAAI,EAAS,GAE3B,EAAc,OAAO,EAAS,EAEzB;IACP;IAGE,KAAoB,MAAqB;AAC7C,KAAsB,MAAsB;GAC1C,IAAM,IAAgB,IAAI,IAAI,EAAkB;AAQhD,UAPA,EAAgB,SAAS,MAAW;AAClC,IAAI,IACF,EAAc,IAAI,EAAO,GAAG,GAE5B,EAAc,OAAO,EAAO,GAAG;KAEjC,EACK;IACP;;AA2DJ,QAxDK,IAcD,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,6BAA6B,yBAAyB,EAAI,CAAA;EAChF,CAAA,GAIL,EAAc,EAAY,YAAY,GAUvC,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,wBAAwB,qBAAqB,EAAI,CAAA;EACvE,CAAA,GAIN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,SAAS,EAAM;GACf,QAAQ;IACN,OAAO,EAAG,gBAAgB,QAAQ;IAClC,eAAe;AACR,QAAS;;IAEjB;GACD,CAAA;EACE,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,sBAAsB,UAAU;OACjC,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,yBACA,mGACD;OACC,CAAA,CACA;SAEL,KAAiB,EAAc,EAAY,cAAc,IACxD,kBAAC,UAAD;MAAQ,MAAK;MACX,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,iBAAiB;MACvE,WAAU;gBAFZ,CAIE,kBAAC,GAAD;OAAM,MAAM;OAAI,eAAY;OAAS,CAAA,EACrC,kBAAC,QAAD;OAAM,WAAU;iBAAe,EAAG,yBAAyB,gBAAgB;OAAQ,CAAA,CAC5E;QAEP;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,aAAa,EAAG,kCAAkC,iCAAiC;OACnF,OAAO,EAAG,4BAA4B,8BAA8B;OACpE,SAAQ;OACR,WAAU;OACV,CAAA,EAEF,kBAAC,UAAD;OAAQ,MAAK;OACX,eAAe;AACR,WAAS;;OAEhB,WAAU;OACV,cAAY,EAAG,wBAAwB,kBAAkB;OACzD,OAAO,EAAG,wBAAwB,kBAAkB;iBAEpD,kBAAC,GAAD;QAAW,MAAM;QAAI,eAAY;QAAS,CAAA;OACnC,CAAA,CACL;SAEL,EAAgB,SAAS,IACxB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,GAAD;SACE,SAAS;SACT,eAAe;SACf,UAAU;SACV,WAAW,EAAG,0BAA0B,qBAAqB;SAC7D,CAAA,EACF,kBAAC,QAAD,EAAA,UAAO,EAAG,2BAA2B,qBAAqB,EAAQ,CAAA,CAC5D;WAEP,EAAkB,OAAO,IACxB,kBAAC,UAAD;QAAQ,MAAK;QACX,eAAe,kBAAqB,IAAI,KAAK,CAAC;QAC9C,WAAU;kBAET,EAAG,yBAAyB,kBAAkB;QACxC,CAAA,GACP,KACA;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACG,EAAkB;SAAK;SAAE,EAAG,mBAAmB,WAAW;SACtD;WACN,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;QAAQ,MAAK;QACX,SAAS;QACT,UAAU,EAAgB,WAAW,KAAK;QAC1C,WAAU;kBAHZ;SAKE,kBAAC,GAAD;UAAQ,MAAM;UAAI,eAAY;UAAS,CAAA;SACvC,kBAAC,QAAD,EAAA,UAAO,EAAG,yBAAyB,kBAAkB,EAAQ,CAAA;SAC5D,EAAgB,SAAS,IAAI,kBAAC,QAAD,EAAA,UAAA;UAAM;UAAE,EAAgB;UAAO;UAAQ,EAAA,CAAA,GAAG;SACjE;UAEP;SACF;UACJ,KACA;;IAEL,EAAgB,WAAW,IAC1B,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD;OAAQ,MAAM;OAAI,eAAY;OAAS,CAAA;MAC7C,OAAO,EAAG,2BAA2B,mBAAmB;MACxD,aAAa,EACX,iCACA,kFACD;MACD,QACE,KAAiB,EAAc,EAAY,cAAc,GACrD;OACE,OAAO,EAAG,yBAAyB,gBAAgB;OACnD,eAAe,EAAS,GAAG,EAAS,GAAG,EAAe,iBAAiB;OACxE,GACD,KAAA;MAEN,CAAA;KACE,CAAA,GAEN,kBAAA,IAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAgB,KAAK,MACpB,kBAAC,WAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,SAAS,EAAkB,IAAI,EAAO,GAAG;QACzC,WAAW,MAAY,EAAsB,EAAO,IAAI,EAAQ;QAChE,WAAW,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;QACzE,CAAA,EAEF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD;UAAQ,MAAM;UAAI,eAAY;UAAS,CAAA;SACnC,CAAA,EAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAO;WACL,CAAA,EACL,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA,CAClC;aACN,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,EAAG,yBAAyB,YAAY;WAAC;WAAG,EAAO;WAClD;YACA,EAAA,CAAA,EAEN,kBAAC,MAAD;UAAI,WAAU;oBAAd,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAG,0BAA0B,aAAa;WACxC,CAAA,EACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAO,aAAa,EAAG,uBAAuB,MAAM;WAClD,CAAA,CACD,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAG,uBAAuB,SAAS;WACjC,CAAA,EACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAW,EAAO,SAAS;WACzB,CAAA,CACD,EAAA,CAAA,CACH;YACD;WACF;UACF;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QAAQ,MAAK;QACX,eACE,EAAS,GAAG,EAAS,GAAG,EAAe,WAAW,EAAO,SAAS;QAEpE,WAAU;kBAJZ,CAME,kBAAC,GAAD;SAAK,MAAM;SAAI,eAAY;SAAS,CAAA,EACnC,EAAG,eAAe,OAAO,CACnB;WACR,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;QAAQ,MAAK;QACX,eAAe,EAAa,EAAO,OAAO;QAC1C,UAAU;QACV,WAAU;kBAHZ,CAKE,kBAAC,GAAD;SAAQ,MAAM;SAAI,eAAY;SAAS,CAAA,EACtC,EAAG,0BAA0B,SAAS,CAChC;UAEP;SACE;QAvEH,EAAO,GAuEJ,CACV;KACE,CAAA,EAEN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,SAAD;QAAO,WAAU;kBACf,kBAAC,MAAD,EAAA,UAAA;SACE,kBAAC,MAAD;UAAI,WAAU;oBACZ,kBAAC,GAAD;WACE,SAAS;WACT,eAAe;WACf,UAAU;WACV,WAAW,EAAG,0BAA0B,qBAAqB;WAC7D,CAAA;UACC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,4BAA4B,OAAO;UACpC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,4BAA4B,OAAO;UACpC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,0BAA0B,aAAa;UACxC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,uBAAuB,SAAS;UACjC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,kBAAkB,UAAU;UAC7B,CAAA;SACF,EAAA,CAAA;QACC,CAAA,EACR,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAgB,KAAK,MACpB,kBAAC,MAAD;SAEE,WAAU;mBAFZ;UAIE,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,GAAD;YACE,SAAS,EAAkB,IAAI,EAAO,GAAG;YACzC,WAAW,MAAY,EAAsB,EAAO,IAAI,EAAQ;YAChE,WAAW,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;YACzE,CAAA;WACC,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,OAAD;aAAK,WAAU;uBACb,kBAAC,GAAD;cAAQ,MAAM;cAAI,eAAY;cAAS,CAAA;aACnC,CAAA,EACN,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBACV,EAAO;cACN,CAAA,EACJ,kBAAC,KAAD;cAAG,WAAU;wBAAb;eACG,EAAG,kBAAkB,KAAK;eAAC;eAAG,EAAO;eACpC;gBACA;eACF;;WACH,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,GAAD,EAAiB,MAAM,EAAO,MAAQ,CAAA;WACnC,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAO,aAAa,EAAG,uBAAuB,MAAM;WAClD,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAW,EAAO,SAAS;WACzB,CAAA;UACL,kBAAC,MAAD;WAAI,WAAU;qBACZ,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,UAAD;aAAQ,MAAK;aACX,eACE,EAAS,GAAG,EAAS,GAAG,EAAe,WAAW,EAAO,SAAS;aAEpE,WAAU;aACV,cAAY,GAAG,EAAG,2BAA2B,cAAc,CAAC,GAAG,EAAO;aACtE,OAAO,GAAG,EAAG,2BAA2B,cAAc,CAAC,GAAG,EAAO;uBAEjE,kBAAC,GAAD;cAAK,MAAM;cAAI,eAAY;cAAS,CAAA;aAC7B,CAAA,EACR,EAAc,EAAY,cAAc,IACvC,kBAAC,UAAD;aAAQ,MAAK;aACX,eAAe,EAAa,EAAO,OAAO;aAC1C,UAAU;aACV,WAAU;aACV,cAAY,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;aAC1E,OAAO,GAAG,EAAG,6BAA6B,gBAAgB,CAAC,GAAG,EAAO;uBAErE,kBAAC,GAAD;cAAQ,MAAM;cAAI,eAAY;cAAS,CAAA;aAChC,CAAA,CAEP;;WACH,CAAA;UACF;WA3DE,EAAO,GA2DT,CACL;QACI,CAAA,CACF;;MACJ,CAAA;KACF,CAAA,CACL,EAAA,CAAA;IAED;MAEL,EACG;MAvUJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,6BAA6B,6CAA6C;GAC1E,CAAA;EACF,CAAA,GA1BN,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,SAAS,EAAG,2BAA2B,8CAA8C;GACrF,QAAQ;IACN,OAAO,EAAG,kCAAkC,sBAAsB;IAClE,eAAe,EAAS,EAAS;IAClC;GACD,CAAA;EACE,CAAA"}
|
|
1
|
+
{"version":3,"file":"MembersListPage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Eye, Mail, RefreshCw, Shield, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { toast } from 'sonner';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMembers, useRemoveMember } from '../hooks/useMembers';\nimport { MemberRoleBadge } from '../components/MemberRoleBadge';\nimport { SearchInput } from '../../../shared/components/input/SearchInput';\nimport { SelectionCheckbox } from '../../../shared/components/input/SelectionCheckbox';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useDebouncedValue } from '../../../shared/hooks/useDebouncedValue';\nimport { useConfirmDialog } from '../../../shared/components/dialog/ConfirmDialog';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nfunction useFormatDate() {\n const tr = useTr();\n return (date?: string) => {\n if (!date) return tr('common.status.pending', 'Pending');\n return new Date(date).toLocaleDateString();\n };\n}\n\nexport function MembersListPage() {\n const tr = useTr();\n const formatDate = useFormatDate();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n // Follow the platform default-allow feature flag policy: unseeded flags must\n // not disable shipped invite actions, while explicit false still gates them.\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n const { organizationId } = useParams<{ organizationId: string }>();\n const [searchQuery, setSearchQuery] = useState('');\n const debouncedSearchQuery = useDebouncedValue(searchQuery, 250);\n const [selectedMemberIds, setSelectedMemberIds] = useState<Set<string>>(new Set());\n const { openConfirmDialog, ConfirmDialog } = useConfirmDialog();\n\n const { members, loading, error, refetch } = useMembers(organizationId ?? '', 50, 0);\n const { removeMember, loading: deleteLoading } = useRemoveMember();\n\n const filteredMembers = useMemo(() => {\n if (!debouncedSearchQuery) {\n return members;\n }\n\n const normalizedQuery = debouncedSearchQuery.toLowerCase();\n return members.filter(\n (member) =>\n member.userId.toLowerCase().includes(normalizedQuery) ||\n member.role.toLowerCase().includes(normalizedQuery)\n );\n }, [debouncedSearchQuery, members]);\n\n useEffect(() => {\n setSelectedMemberIds((previousSelection) => {\n const visibleIds = new Set(filteredMembers.map((member) => member.id));\n const nextSelection = new Set(\n [...previousSelection].filter((memberId) => visibleIds.has(memberId))\n );\n\n if (nextSelection.size === previousSelection.size) {\n return previousSelection;\n }\n\n return nextSelection;\n });\n }, [filteredMembers]);\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const allVisibleSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelected =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) && !allVisibleSelected;\n\n const executeRemoveMembers = async (userIds: string[]) => {\n if (!organizationId) return;\n\n try {\n const results = await Promise.allSettled(\n userIds.map(async (userId) => {\n const result = await removeMember(organizationId, userId);\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to remove member');\n }\n })\n );\n\n const rejectedResult = results.find((result) => result.status === 'rejected');\n if (rejectedResult?.status === 'rejected') {\n throw rejectedResult.reason;\n }\n\n setSelectedMemberIds(new Set());\n await refetch();\n toast.success(\n userIds.length === 1\n ? tr('members.remove.successSingle', 'Member removed successfully')\n : `${userIds.length} ${tr('members.remove.successMany', 'members removed successfully')}`\n );\n } catch (removeError) {\n toast.error(\n removeError instanceof Error\n ? removeError.message\n : tr('members.remove.failed', 'Failed to remove member')\n );\n }\n };\n\n const handleRemove = (userId: string) => {\n openConfirmDialog({\n title: tr('members.remove.title', 'Remove member'),\n description: `${tr('members.remove.descriptionPrefix', 'Remove')} ${userId} ${tr('members.remove.descriptionSuffix', 'from this organization? Workspace access inherited from this membership will be impacted.')}`,\n confirmText: tr('members.remove.confirm', 'Remove'),\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers([userId]);\n },\n });\n };\n\n const handleBulkRemove = () => {\n if (selectedMembers.length === 0) return;\n\n openConfirmDialog({\n title: tr('members.remove.bulkTitle', 'Remove selected members'),\n description: `${tr('members.remove.bulkPrefix', 'Remove')} ${selectedMembers.length} ${tr(\n selectedMembers.length === 1\n ? 'members.remove.bulkSuffixSingle'\n : 'members.remove.bulkSuffixMany',\n selectedMembers.length === 1\n ? 'selected member from this organization?'\n : 'selected members from this organization?'\n )}`,\n confirmText: `${tr('members.remove.confirm', 'Remove')} ${selectedMembers.length}`,\n variant: 'danger',\n onConfirm: () => {\n void executeRemoveMembers(selectedMembers.map((member) => member.userId));\n },\n });\n };\n\n const toggleMemberSelection = (memberId: string, checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n if (checked) {\n nextSelection.add(memberId);\n } else {\n nextSelection.delete(memberId);\n }\n return nextSelection;\n });\n };\n\n const toggleAllMembers = (checked: boolean) => {\n setSelectedMemberIds((previousSelection) => {\n const nextSelection = new Set(previousSelection);\n filteredMembers.forEach((member) => {\n if (checked) {\n nextSelection.add(member.id);\n } else {\n nextSelection.delete(member.id);\n }\n });\n return nextSelection;\n });\n };\n\n if (!organizationId) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={tr('members.list.missingOrg', 'Organization ID is required to view members')}\n action={{\n label: tr('members.list.goToOrganizations', 'Go to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.MEMBER_LIST)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr('members.list.noPermission', \"You don't have permission to view members.\")}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState message={tr('members.list.loading', 'Loading members...')} />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetch();\n },\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.list.title', 'Members')}\n </h1>\n <p className=\"max-w-2xl text-[var(--color-text-secondary)]\">\n {tr(\n 'members.list.subtitle',\n 'Manage organization members, inspect their roles, and invite more people into this organization.'\n )}\n </p>\n </div>\n\n {inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${organizationId}/members/invite`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Mail size={18} aria-hidden=\"true\" />\n <span className=\"font-medium\">{tr('members.invite.button', 'Invite Member')}</span>\n </button>\n )}\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('members.list.searchPlaceholder', 'Search by member ID or role...')}\n label={tr('members.list.searchLabel', 'Search organization members')}\n inputId=\"organization-members-search\"\n className=\"flex-1\"\n />\n\n <button\n type=\"button\"\n onClick={() => {\n void refetch();\n }}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] px-4 py-2 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)] active:scale-95\"\n aria-label={tr('members.list.refresh', 'Refresh members')}\n title={tr('members.list.refresh', 'Refresh members')}\n >\n <RefreshCw size={18} aria-hidden=\"true\" />\n </button>\n </div>\n\n {filteredMembers.length > 0 ? (\n <div className=\"mt-4 flex flex-col gap-3 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-[var(--color-text-primary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n <span>{tr('common.selectAllVisible', 'Select all visible')}</span>\n </label>\n\n {selectedMemberIds.size > 0 ? (\n <button\n type=\"button\"\n onClick={() => setSelectedMemberIds(new Set())}\n className=\"text-sm text-[var(--color-text-secondary)] transition-colors hover:text-[var(--color-text-primary)]\"\n >\n {tr('common.clearSelection', 'Clear selection')}\n </button>\n ) : null}\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\n {selectedMemberIds.size} {tr('common.selected', 'selected')}\n </span>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={handleBulkRemove}\n disabled={selectedMembers.length === 0 || deleteLoading}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n <span>{tr('common.removeSelected', 'Remove Selected')}</span>\n {selectedMembers.length > 0 ? <span>({selectedMembers.length})</span> : null}\n </button>\n )}\n </div>\n </div>\n ) : null}\n </div>\n\n {filteredMembers.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6 shadow-[var(--shadow-elevation-1)]\">\n <EmptyState\n icon={<Shield size={40} aria-hidden=\"true\" />}\n title={tr('members.list.emptyTitle', 'No members found')}\n description={tr(\n 'members.list.emptyDescription',\n 'Invite someone to this organization to grant them access and assign their role.'\n )}\n action={\n inviteEnabled && hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? {\n label: tr('members.invite.button', 'Invite Member'),\n onClick: () => navigate(`${basePath}/${organizationId}/members/invite`),\n }\n : undefined\n }\n />\n </div>\n ) : (\n <>\n <div className=\"grid gap-4 md:hidden\">\n {filteredMembers.map((member) => (\n <article\n key={member.id}\n className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-4 shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"flex items-start gap-3\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n\n <div className=\"flex min-w-0 flex-1 gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 flex-1 space-y-3\">\n <div>\n <div className=\"flex flex-wrap items-start gap-2\">\n <h2 className=\"break-all text-base font-semibold text-[var(--color-text-primary)]\">\n {member.userId}\n </h2>\n <MemberRoleBadge role={member.role} />\n </div>\n <p className=\"mt-1 break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('members.list.memberId', 'Member ID')}: {member.id}\n </p>\n </div>\n\n <dl className=\"grid grid-cols-1 gap-3 text-sm\">\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-[var(--color-text-primary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </dd>\n </div>\n <div>\n <dt className=\"text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </dt>\n <dd className=\"mt-1 text-[var(--color-text-primary)]\">\n {formatDate(member.joinedAt)}\n </dd>\n </div>\n </dl>\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-2 border-t border-[var(--color-border-default)] pt-4\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-border-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Eye size={16} aria-hidden=\"true\" />\n {tr('common.view', 'View')}\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"inline-flex min-h-10 items-center gap-2 rounded-lg border border-[var(--color-status-error-border)] px-3 py-2 text-sm text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Trash2 size={16} aria-hidden=\"true\" />\n {tr('members.remove.confirm', 'Remove')}\n </button>\n )}\n </div>\n </article>\n ))}\n </div>\n\n <div className=\"hidden overflow-hidden rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)] md:block\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-[var(--color-bg-subtle)]\">\n <tr>\n <th className=\"w-12 px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllMembers}\n ariaLabel={tr('members.list.selectAll', 'Select all members')}\n />\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.user', 'User')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.column.role', 'Role')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.invitedBy', 'Invited by')}\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('members.list.joined', 'Joined')}\n </th>\n <th className=\"px-6 py-3 text-right text-xs font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]\">\n {tr('common.actions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[var(--color-border-default)]\">\n {filteredMembers.map((member) => (\n <tr\n key={member.id}\n className=\"transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <td className=\"px-6 py-4 align-top\">\n <SelectionCheckbox\n checked={selectedMemberIds.has(member.id)}\n onChange={(checked) => toggleMemberSelection(member.id, checked)}\n ariaLabel={`${tr('members.list.selectMember', 'Select member')} ${member.userId}`}\n />\n </td>\n <td className=\"px-6 py-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Shield size={18} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"break-all font-medium text-[var(--color-text-primary)]\">\n {member.userId}\n </p>\n <p className=\"break-all font-mono text-xs text-[var(--color-text-muted)]\">\n {tr('common.idLabel', 'ID')}: {member.id}\n </p>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 align-top\">\n <MemberRoleBadge role={member.role} />\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {member.invitedBy || tr('common.notAvailable', 'N/A')}\n </td>\n <td className=\"px-6 py-4 align-top text-sm text-[var(--color-text-secondary)]\">\n {formatDate(member.joinedAt)}\n </td>\n <td className=\"px-6 py-4 align-top\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/${organizationId}/members/${member.userId}`)\n }\n className=\"rounded-lg p-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:bg-[var(--color-bg-muted)] hover:text-[var(--color-text-primary)]\"\n aria-label={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n title={`${tr('members.list.viewMember', 'View member')} ${member.userId}`}\n >\n <Eye size={18} aria-hidden=\"true\" />\n </button>\n {hasPermission(PERMISSIONS.MEMBER_REMOVE) && (\n <button\n type=\"button\"\n onClick={() => handleRemove(member.userId)}\n disabled={deleteLoading}\n className=\"rounded-lg p-2 text-[var(--color-status-error-text)] transition-all duration-200 hover:bg-[var(--color-status-error-bgSubtle)] disabled:cursor-not-allowed disabled:opacity-50\"\n aria-label={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n title={`${tr('members.list.removeMember', 'Remove member')} ${member.userId}`}\n >\n <Trash2 size={18} aria-hidden=\"true\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </>\n )}\n </div>\n\n {ConfirmDialog}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -166,6 +166,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
166
166
|
...w,
|
|
167
167
|
tenantId: e.target.value
|
|
168
168
|
}),
|
|
169
|
+
"aria-label": x("organizations.form.tenant", "Tenant"),
|
|
169
170
|
className: "\n w-full px-3 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
170
171
|
placeholder: x("organizations.form.tenantPlaceholder", "Enter tenant ID"),
|
|
171
172
|
disabled: b,
|
|
@@ -198,6 +199,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
198
199
|
...w,
|
|
199
200
|
name: e.target.value
|
|
200
201
|
}),
|
|
202
|
+
"aria-label": x("organizations.form.name", "Organization Name"),
|
|
201
203
|
className: "\n w-full px-3 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
202
204
|
placeholder: x("organizations.form.namePlaceholder", "Enter organization name"),
|
|
203
205
|
disabled: b,
|
|
@@ -226,6 +228,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
226
228
|
id: "slug",
|
|
227
229
|
type: "text",
|
|
228
230
|
value: w.slug,
|
|
231
|
+
"aria-label": x("common.slug", "Slug"),
|
|
229
232
|
onChange: (e) => {
|
|
230
233
|
k(!0), N.current = null, P.current = null, T({
|
|
231
234
|
...w,
|
|
@@ -258,6 +261,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
258
261
|
...w,
|
|
259
262
|
description: e.target.value
|
|
260
263
|
}),
|
|
264
|
+
"aria-label": x("common.description", "Description"),
|
|
261
265
|
rows: 4,
|
|
262
266
|
className: "\n w-full px-3 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
263
267
|
placeholder: x("organizations.form.descriptionPlaceholder", "Enter organization description"),
|
|
@@ -276,6 +280,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
276
280
|
...w,
|
|
277
281
|
logo: e.target.value
|
|
278
282
|
}),
|
|
283
|
+
"aria-label": x("organizations.form.logoUrl", "Logo URL"),
|
|
279
284
|
className: "\n w-full px-3 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
280
285
|
placeholder: "https://example.com/logo.png",
|
|
281
286
|
disabled: b,
|
|
@@ -293,6 +298,7 @@ function h({ organization: h, tenantId: g, tenantName: _, onSubmit: v, onCancel:
|
|
|
293
298
|
...w,
|
|
294
299
|
website: e.target.value
|
|
295
300
|
}),
|
|
301
|
+
"aria-label": x("organizations.detail.website", "Website"),
|
|
296
302
|
className: "\n w-full px-3 py-2\n bg-[var(--color-bg-surface)]\n border border-[var(--color-border-default)]\n rounded-lg\n text-[var(--color-text-primary)]\n placeholder:text-[var(--color-text-muted)]\n focus:outline-none focus:ring-2 focus:ring-[var(--color-border-focus)]\n disabled:bg-[var(--color-bg-muted)] disabled:cursor-not-allowed\n ",
|
|
297
303
|
placeholder: "https://example.com",
|
|
298
304
|
disabled: b,
|