Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions packages/worker/client/routes/account-management-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
cardTitleCss,
descriptionCss,
fieldLabelCss,
getSecondaryButtonCss,
layoutMaxWidths,
} from '#client/styles/style-primitives.ts'

Expand Down Expand Up @@ -88,6 +89,74 @@ export function AccountManagementHeader(
)
}

const adminNavItems = [
{ href: '/admin/users', label: 'Users', paths: ['/admin', '/admin/users'] },
{ href: '/admin/invites', label: 'Invites', paths: ['/admin/invites'] },
{ href: '/admin/roles', label: 'Roles', paths: ['/admin/roles'] },
{
href: '/admin/community-reports',
label: 'Community reports',
paths: ['/admin/community-reports'],
},
{ href: '/admin/usage', label: 'Usage', paths: ['/admin/usage'] },
{
href: '/admin/system-email',
label: 'System email',
paths: ['/admin/system-email'],
},
] as const

type AdminPageHeaderProps = {
title: string
description: string
currentHref: string
}

export function AdminPageHeader(handle: Handle<AdminPageHeaderProps>) {
const secondaryButtonCss = getSecondaryButtonCss()

return () => {
const currentPath = new URL(handle.props.currentHref, 'http://localhost')
.pathname

return (
<>
<AccountManagementHeader
title={handle.props.title}
description={handle.props.description}
/>
<nav
aria-label="Admin sections"
mix={css({ display: 'flex', gap: spacing.sm, flexWrap: 'wrap' })}
>
{adminNavItems.map((item) => {
const isCurrent = item.paths.some((path) => path === currentPath)
return (
<a
key={item.href}
href={item.href}
aria-current={isCurrent ? 'page' : undefined}
mix={css({
...secondaryButtonCss,
textDecoration: 'none',
...(isCurrent
? {
borderColor: colors.primary,
backgroundColor: colors.primarySoftest,
}
: {}),
})}
>
{item.label}
</a>
)
})}
</nav>
</>
)
}
}

type AccountManagementMessageProps = {
children: AccountManagementSlot
tone?: 'info' | 'error'
Expand Down
33 changes: 3 additions & 30 deletions packages/worker/client/routes/admin-community-reports.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
inputCss,
} from '#client/styles/style-primitives.ts'
import {
AccountManagementHeader,
AccountManagementMessage,
AccountManagementShell,
AdminPageHeader,
} from './account-management-components.tsx'
import { type AdminCommunityReportsLoaderData } from '#app/loader-data.ts'
import {
Expand Down Expand Up @@ -276,37 +276,10 @@ export function AdminCommunityReportsRoute(handle: Handle) {

return (
<AccountManagementShell>
<AccountManagementHeader
<AdminPageHeader
title="Community reports"
description="Review open reports and moderate community listings."
actions={
<>
<a
href="/admin/users"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
View users
</a>
<a
href="/admin/roles"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
View roles
</a>
<a
href="/admin/usage"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Usage
</a>
<a
href="/admin/system-email"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
System email
</a>
</>
}
currentHref={currentHref}
/>

<div mix={css({ display: 'flex', gap: spacing.sm, flexWrap: 'wrap' })}>
Expand Down
33 changes: 3 additions & 30 deletions packages/worker/client/routes/admin-invites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
inputCss,
} from '#client/styles/style-primitives.ts'
import {
AccountManagementHeader,
AccountManagementMessage,
AccountManagementPanel,
AccountManagementShell,
AdminPageHeader,
MetadataGrid,
} from './account-management-components.tsx'
import {
Expand Down Expand Up @@ -241,37 +241,10 @@ export function AdminInvitesRoute(handle: Handle) {

return (
<AccountManagementShell>
<AccountManagementHeader
<AdminPageHeader
title="Admin invites"
description="Create and revoke launch-cohort invite codes for production signup."
actions={
<>
<a
href="/admin/users"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Users
</a>
<a
href="/admin/roles"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Roles
</a>
<a
href="/admin/usage"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Usage
</a>
<a
href="/admin/system-email"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
System email
</a>
</>
}
currentHref={currentHref}
/>
{status === 'loading' ? (
<p mix={css({ color: colors.textMuted, margin: 0 })}>
Expand Down
36 changes: 3 additions & 33 deletions packages/worker/client/routes/admin-roles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { tryConsumeRouteLoaderData } from '#client/loader-data-context.tsx'
import { consumeStaleNavigationData } from '#client/navigation-data.ts'
import { readJson } from '#client/routes/account-approval-shared.ts'
import { colors, spacing, typography } from '#client/styles/tokens.ts'
import { getSecondaryButtonCss } from '#client/styles/style-primitives.ts'
import {
AccountManagementHeader,
AccountManagementMessage,
AccountManagementPanel,
AccountManagementShell,
AdminPageHeader,
} from './account-management-components.tsx'
import { type AdminRolesLoaderData } from '#app/loader-data.ts'
import {
Expand Down Expand Up @@ -111,8 +110,6 @@ export function AdminRolesRoute(handle: Handle) {
return true
}

const secondaryButtonCss = getSecondaryButtonCss()

let lastSeenHref = ''

return () => {
Expand Down Expand Up @@ -143,37 +140,10 @@ export function AdminRolesRoute(handle: Handle) {

return (
<AccountManagementShell>
<AccountManagementHeader
<AdminPageHeader
title="Admin roles"
description="Read-only view of roles and the permissions attached to each."
actions={
<>
<a
href="/admin/users"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
View users
</a>
<a
href="/admin/usage"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Usage
</a>
<a
href="/admin/system-email"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
System email
</a>
<a
href="/admin/community-reports"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Community reports
</a>
</>
}
currentHref={currentHref}
/>
{status === 'loading' ? (
<p mix={css({ color: colors.textMuted, margin: 0 })}>
Expand Down
39 changes: 4 additions & 35 deletions packages/worker/client/routes/admin-system-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import { tryConsumeRouteLoaderData } from '#client/loader-data-context.tsx'
import { consumeStaleNavigationData } from '#client/navigation-data.ts'
import { readJson } from '#client/routes/account-approval-shared.ts'
import { colors, spacing, typography } from '#client/styles/tokens.ts'
import { cardCss } from '#client/styles/style-primitives.ts'
import {
cardCss,
getSecondaryButtonCss,
} from '#client/styles/style-primitives.ts'
import {
AccountManagementHeader,
AccountManagementMessage,
AccountManagementPanel,
AccountManagementShell,
AdminPageHeader,
MetadataGrid,
} from './account-management-components.tsx'
import { type AdminSystemEmailLoaderData } from '#app/loader-data.ts'
Expand Down Expand Up @@ -135,7 +132,6 @@ export function AdminSystemEmailRoute(handle: Handle) {
return true
}

const secondaryButtonCss = getSecondaryButtonCss()
const tableCss = {
width: '100%',
borderCollapse: 'collapse' as const,
Expand Down Expand Up @@ -183,37 +179,10 @@ export function AdminSystemEmailRoute(handle: Handle) {

return (
<AccountManagementShell maxWidth="min(100%, 92rem)">
<AccountManagementHeader
<AdminPageHeader
title="Admin system email"
description="Operator-owned inboxes for reserved platform addresses. These messages are not user account data."
actions={
<>
<a
href="/admin/users"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Users
</a>
<a
href="/admin/usage"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Usage
</a>
<a
href="/admin/invites"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Invites
</a>
<a
href="/admin/roles"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Roles
</a>
</>
}
currentHref={currentHref}
/>
{status === 'loading' ? (
<p mix={css({ color: colors.textMuted, margin: 0 })}>
Expand Down
39 changes: 4 additions & 35 deletions packages/worker/client/routes/admin-usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import { tryConsumeRouteLoaderData } from '#client/loader-data-context.tsx'
import { consumeStaleNavigationData } from '#client/navigation-data.ts'
import { readJson } from '#client/routes/account-approval-shared.ts'
import { colors, radius, spacing, typography } from '#client/styles/tokens.ts'
import { cardCss } from '#client/styles/style-primitives.ts'
import {
cardCss,
getSecondaryButtonCss,
} from '#client/styles/style-primitives.ts'
import {
AccountManagementHeader,
AccountManagementMessage,
AccountManagementPanel,
AccountManagementShell,
AdminPageHeader,
MetadataGrid,
} from './account-management-components.tsx'
import {
Expand Down Expand Up @@ -167,7 +164,6 @@ export function AdminUsageRoute(handle: Handle) {
return true
}

const secondaryButtonCss = getSecondaryButtonCss()
const tableCss = {
width: '100%',
borderCollapse: 'collapse' as const,
Expand Down Expand Up @@ -216,37 +212,10 @@ export function AdminUsageRoute(handle: Handle) {

return (
<AccountManagementShell maxWidth="min(100%, 92rem)">
<AccountManagementHeader
<AdminPageHeader
title="Admin usage"
description="Read-only account metadata for usage, quota counters, and resource counts. User content is never shown."
actions={
<>
<a
href="/admin/users"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Users
</a>
<a
href="/admin/system-email"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
System email
</a>
<a
href="/admin/invites"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Invites
</a>
<a
href="/admin/roles"
mix={css({ ...secondaryButtonCss, textDecoration: 'none' })}
>
Roles
</a>
</>
}
currentHref={currentHref}
/>
{status === 'loading' ? (
<p mix={css({ color: colors.textMuted, margin: 0 })}>
Expand Down
Loading
Loading