diff --git a/frontend/src/admin/AdminApp.vue b/frontend/src/admin/AdminApp.vue index 51b56e7..29882ed 100644 --- a/frontend/src/admin/AdminApp.vue +++ b/frontend/src/admin/AdminApp.vue @@ -5,6 +5,10 @@ import CredentialList from '@/components/CredentialList.vue' import UserBasicInfo from '@/components/UserBasicInfo.vue' import RegistrationLinkModal from '@/components/RegistrationLinkModal.vue' import StatusMessage from '@/components/StatusMessage.vue' +import AdminOverview from './AdminOverview.vue' +import AdminOrgDetail from './AdminOrgDetail.vue' +import AdminUserDetail from './AdminUserDetail.vue' +import AdminDialogs from './AdminDialogs.vue' import { useAuthStore } from '@/stores/auth' const info = ref(null) @@ -467,221 +471,51 @@ async function submitDialog() {

Insufficient permissions.

-
-

Organizations

-
- -
- - - - - - - - - - - - - - - - - -
NameRolesMembersActions
- {{ o.display_name }} - - {{ o.roles.length }}{{ o.roles.reduce((acc,r)=>acc + r.users.length,0) }} - -
-
+ -
- -
{{ userDetail.error }}
- -
- - - -
-

Use the token dialog to register a new credential for the member.

- -
-
-

- {{ selectedOrg.display_name }} - -

-
+ + -
-
-
-
Permission
-
- {{ r.display_name }} -
-
➕
- - -
-
-

Toggle which permissions each role grants.

-
-
-
-
- - {{ r.display_name }} - - -
- -
-
- -
-

No members

- -
-
-
-
- -
-

Permissions

-
-
-
-
Permission
-
- {{ o.display_name }} -
- - -
-
-

Toggle which permissions each organization can grant to its members.

-
-
- -
- - - - - - - - - - - - - - - -
PermissionMembersActions
-
- {{ p.display_name }} - -
-
- {{ p.id }} - -
-
{{ permissionSummary[p.id]?.userCount || 0 }} - -
-
@@ -689,70 +523,12 @@ async function submitDialog() { - + @@ -762,134 +538,4 @@ async function submitDialog() { .admin-section { margin-top: var(--space-xl); } .admin-section-body { display: flex; flex-direction: column; gap: var(--space-xl); } .admin-panels { display: flex; flex-direction: column; gap: var(--space-xl); } -.permissions-section { margin-bottom: var(--space-xl); } -.permissions-section h2 { margin-bottom: var(--space-md); } -.actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; } -.actions button { width: auto; } -.org-table a { text-decoration: none; color: var(--color-link); } -.org-table a:hover { text-decoration: underline; } -.perm-name-cell { display: flex; flex-direction: column; gap: 0.3rem; } -.perm-title { font-weight: 600; color: var(--color-heading); } -.perm-id-info { font-size: 0.8rem; color: var(--color-text-muted); } -.plus-btn { background: var(--color-accent-soft); color: var(--color-accent); border: none; border-radius: var(--radius-sm); padding: 0.25rem 0.45rem; font-size: 1.1rem; cursor: pointer; } -.plus-btn:hover { background: rgba(37, 99, 235, 0.18); } -.user-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-xs); } -.user-chip { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 0.45rem 0.6rem; display: flex; justify-content: space-between; gap: var(--space-sm); cursor: grab; } -.user-chip .meta { font-size: 0.7rem; color: var(--color-text-muted); } -.empty-role { border: 1px dashed var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-sm); display: flex; flex-direction: column; gap: var(--space-xs); align-items: flex-start; } -.icon-btn { background: none; border: none; color: var(--color-text-muted); padding: 0.2rem; border-radius: var(--radius-sm); cursor: pointer; transition: background 0.2s ease, color 0.2s ease; } -.icon-btn:hover { color: var(--color-heading); background: var(--color-surface-muted); } -.delete-icon { color: var(--color-danger); } -.delete-icon:hover { background: var(--color-danger-bg); color: var(--color-danger-text); } -.matrix-wrapper { margin: var(--space-md) 0; padding: var(--space-lg); } -.matrix-scroll { overflow-x: auto; } -.matrix-hint { font-size: 0.8rem; color: var(--color-text-muted); } -.perm-matrix-grid { display: inline-grid; gap: 0.25rem; align-items: stretch; } -.perm-matrix-grid > * { padding: 0.35rem 0.45rem; font-size: 0.75rem; } -.perm-matrix-grid .grid-head { color: var(--color-text-muted); text-transform: uppercase; font-weight: 600; letter-spacing: 0.05em; } -.perm-matrix-grid .perm-head { display: flex; align-items: flex-end; justify-content: flex-start; padding: 0.35rem 0.45rem; font-size: 0.75rem; } -.perm-matrix-grid .role-head { display: flex; align-items: flex-end; justify-content: center; } -.perm-matrix-grid .role-head span { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 0.65rem; } -.perm-matrix-grid .org-head { display: flex; align-items: flex-end; justify-content: center; } -.perm-matrix-grid .org-head span { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 0.65rem; } -.perm-matrix-grid .add-role-head, -.perm-matrix-grid .add-permission-head { cursor: pointer; } -.perm-name { font-weight: 600; color: var(--color-heading); padding: 0.35rem 0.45rem; font-size: 0.75rem; } -.perm-orgs { gap: 0.5rem; } -.perm-orgs-list { display: flex; flex-wrap: wrap; gap: 0.4rem; } -.org-pill { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.2rem 0.55rem; border-radius: 999px; background: var(--color-surface-muted); border: 1px solid var(--color-border); font-size: 0.75rem; } -.pill-x { background: none; border: none; color: var(--color-danger); cursor: pointer; } -.pill-x:hover { color: var(--color-danger-text); } -.org-add-wrapper { display: inline-flex; align-items: center; gap: var(--space-xs); position: relative; } -.add-org-btn { background: var(--color-accent-soft); color: var(--color-accent); border: none; border-radius: var(--radius-sm); padding: 0.2rem 0.4rem; cursor: pointer; } -.add-org-btn:hover { background: rgba(37, 99, 235, 0.18); } -.org-add-menu { position: absolute; top: calc(100% + var(--space-xs)); right: 0; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: var(--space-xs); min-width: 220px; z-index: 20; } -.org-add-list { display: flex; flex-direction: column; gap: var(--space-xs); max-height: 240px; overflow-y: auto; } -.org-add-item { background: none; border: 1px solid transparent; border-radius: var(--radius-sm); padding: 0.45rem 0.6rem; text-align: left; cursor: pointer; } -.org-add-item:hover { background: var(--color-surface-muted); border-color: var(--color-border-strong); } -.org-add-footer { display: flex; justify-content: flex-end; margin-top: var(--space-xs); } -.org-add-cancel { background: none; border: none; color: var(--color-text-muted); cursor: pointer; } -.display-text { margin-right: var(--space-xs); } -.edit-display-btn { padding: 0.1rem 0.2rem; font-size: 0.8rem; } -.edit-org-btn { padding: 0.1rem 0.2rem; font-size: 0.8rem; margin-left: var(--space-xs); } -.perm-actions { text-align: center; } -.small { font-size: 0.9rem; } -.muted { color: var(--color-text-muted); } -.error { color: var(--color-danger-text); } - -.modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - backdrop-filter: blur(.1rem); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.modal { - background: var(--color-surface); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-xl); - padding: var(--space-lg); - max-width: 500px; - width: 90%; - max-height: 90vh; - overflow-y: auto; -} - -.modal-title { - margin: 0 0 var(--space-md) 0; - font-size: 1.25rem; - font-weight: 600; - color: var(--color-heading); -} - -.modal-form { - display: flex; - flex-direction: column; - gap: var(--space-md); -} - -.modal-form label { - display: flex; - flex-direction: column; - gap: var(--space-xs); - font-weight: 500; -} - -.modal-form input, -.modal-form textarea { - padding: var(--space-sm); - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - background: var(--color-surface); - color: var(--color-text); -} - -.modal-form input:focus, -.modal-form textarea:focus { - outline: none; - border-color: var(--color-accent); - box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1); -} - -.modal-actions { - display: flex; - justify-content: flex-end; - gap: var(--space-sm); - margin-top: var(--space-lg); -} - -@media (max-width: 720px) { - .card.surface { padding: var(--space-md); } - .actions { flex-direction: column; align-items: flex-start; } - .roles-grid { flex-direction: column; } - .org-add-menu { left: 0; right: auto; } -} diff --git a/frontend/src/admin/AdminDialogs.vue b/frontend/src/admin/AdminDialogs.vue new file mode 100644 index 0000000..194b9ae --- /dev/null +++ b/frontend/src/admin/AdminDialogs.vue @@ -0,0 +1,150 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/admin/AdminOrgDetail.vue b/frontend/src/admin/AdminOrgDetail.vue new file mode 100644 index 0000000..31001a9 --- /dev/null +++ b/frontend/src/admin/AdminOrgDetail.vue @@ -0,0 +1,142 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/admin/AdminOverview.vue b/frontend/src/admin/AdminOverview.vue new file mode 100644 index 0000000..89ef123 --- /dev/null +++ b/frontend/src/admin/AdminOverview.vue @@ -0,0 +1,153 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/admin/AdminUserDetail.vue b/frontend/src/admin/AdminUserDetail.vue new file mode 100644 index 0000000..0ce484c --- /dev/null +++ b/frontend/src/admin/AdminUserDetail.vue @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file