|
| 1 | +'use client' |
| 2 | + |
| 3 | +import { useState } from 'react' |
| 4 | +import { Chip, ChipCombobox, ChipLink } from '@sim/emcn' |
| 5 | +import { useQueryStates } from 'nuqs' |
| 6 | +import { |
| 7 | + benchmarkConsoleParams, |
| 8 | + benchmarkConsoleUrlOptions, |
| 9 | + emptyBenchmarkSelection, |
| 10 | +} from '@/app/benchmark/search-params' |
| 11 | +import { Benchmark } from '@/app/o/[organizationId]/benchmark/benchmark' |
| 12 | +import { |
| 13 | + useBenchmarkOrganizations, |
| 14 | + useBenchmarkUsers, |
| 15 | + useBenchmarkWorkspaces, |
| 16 | +} from '@/hooks/queries/benchmarks' |
| 17 | +import { useDebounce } from '@/hooks/use-debounce' |
| 18 | + |
| 19 | +export function BenchmarkConsole() { |
| 20 | + const [{ organizationId, runAsUserId }, setParams] = useQueryStates( |
| 21 | + benchmarkConsoleParams, |
| 22 | + benchmarkConsoleUrlOptions |
| 23 | + ) |
| 24 | + const [organizationSearch, setOrganizationSearch] = useState('') |
| 25 | + const [userSearch, setUserSearch] = useState('') |
| 26 | + const organizations = useBenchmarkOrganizations( |
| 27 | + useDebounce(organizationSearch, 250), |
| 28 | + organizationId |
| 29 | + ) |
| 30 | + const users = useBenchmarkUsers(organizationId, useDebounce(userSearch, 250), runAsUserId) |
| 31 | + const workspaces = useBenchmarkWorkspaces(organizationId, runAsUserId) |
| 32 | + const selectedOrganization = organizations.data?.pages[0]?.selected |
| 33 | + const organizationOptions = [ |
| 34 | + ...(selectedOrganization ? [selectedOrganization] : []), |
| 35 | + ...(organizations.data?.pages |
| 36 | + .flatMap((page) => page.organizations) |
| 37 | + .filter((org) => org.id !== selectedOrganization?.id) ?? []), |
| 38 | + ] |
| 39 | + const selectedUser = users.data?.pages[0]?.selected |
| 40 | + const userOptions = [ |
| 41 | + ...(selectedUser ? [selectedUser] : []), |
| 42 | + ...(users.data?.pages |
| 43 | + .flatMap((page) => page.users) |
| 44 | + .filter((user) => user.id !== selectedUser?.id) ?? []), |
| 45 | + ] |
| 46 | + const error = organizations.error ?? users.error ?? workspaces.error |
| 47 | + |
| 48 | + return ( |
| 49 | + <main className='workspace-root min-h-screen bg-[var(--bg)] text-[var(--text-body)]'> |
| 50 | + <div className='mx-auto flex w-full max-w-chat flex-col gap-5 px-6 pt-8'> |
| 51 | + <div className='flex items-center justify-between gap-4'> |
| 52 | + <h1 className='text-[var(--text-primary)] text-lg'>Benchmarks</h1> |
| 53 | + <ChipLink href='/account/settings/admin'>Back to settings</ChipLink> |
| 54 | + </div> |
| 55 | + <p className='text-[var(--text-muted)] text-small'> |
| 56 | + Run a benchmark with a user’s enterprise access. Your admin session stays unchanged. |
| 57 | + </p> |
| 58 | + <div className='grid gap-4 sm:grid-cols-2'> |
| 59 | + <div className='flex flex-col gap-2'> |
| 60 | + <label htmlFor='benchmark-organization' className='text-small'> |
| 61 | + Organization |
| 62 | + </label> |
| 63 | + <ChipCombobox |
| 64 | + id='benchmark-organization' |
| 65 | + aria-label='Organization' |
| 66 | + value={organizationId} |
| 67 | + options={organizationOptions.map((org) => ({ value: org.id, label: org.name }))} |
| 68 | + placeholder='Select an organization' |
| 69 | + searchable |
| 70 | + isLoading={organizations.isLoading} |
| 71 | + onSearchChange={setOrganizationSearch} |
| 72 | + onChange={(value) => |
| 73 | + setParams({ organizationId: value, runAsUserId: null, ...emptyBenchmarkSelection }) |
| 74 | + } |
| 75 | + /> |
| 76 | + {organizations.hasNextPage && ( |
| 77 | + <Chip |
| 78 | + disabled={organizations.isFetchingNextPage} |
| 79 | + onClick={() => organizations.fetchNextPage()} |
| 80 | + > |
| 81 | + Load more organizations |
| 82 | + </Chip> |
| 83 | + )} |
| 84 | + </div> |
| 85 | + <div className='flex flex-col gap-2'> |
| 86 | + <label htmlFor='benchmark-user' className='text-small'> |
| 87 | + Run as user |
| 88 | + </label> |
| 89 | + <ChipCombobox |
| 90 | + key={organizationId} |
| 91 | + id='benchmark-user' |
| 92 | + aria-label='Run as user' |
| 93 | + value={runAsUserId} |
| 94 | + options={userOptions.map((user) => ({ |
| 95 | + value: user.id, |
| 96 | + label: `${user.name} (${user.email})`, |
| 97 | + }))} |
| 98 | + placeholder='Select a user' |
| 99 | + searchable |
| 100 | + isLoading={users.isLoading} |
| 101 | + disabled={!organizationId} |
| 102 | + onSearchChange={setUserSearch} |
| 103 | + onChange={(value) => setParams({ runAsUserId: value, ...emptyBenchmarkSelection })} |
| 104 | + /> |
| 105 | + {users.hasNextPage && ( |
| 106 | + <Chip disabled={users.isFetchingNextPage} onClick={() => users.fetchNextPage()}> |
| 107 | + Load more users |
| 108 | + </Chip> |
| 109 | + )} |
| 110 | + </div> |
| 111 | + </div> |
| 112 | + {runAsUserId && ( |
| 113 | + <p className='text-[var(--text-muted)] text-small'> |
| 114 | + Running as {selectedUser?.email ?? runAsUserId}. Normal permissions and billing for this |
| 115 | + organization apply. Reports are private to you. |
| 116 | + </p> |
| 117 | + )} |
| 118 | + {error && ( |
| 119 | + <p role='alert' className='text-[var(--text-error)] text-small'> |
| 120 | + {error.message} |
| 121 | + </p> |
| 122 | + )} |
| 123 | + </div> |
| 124 | + {organizationId && runAsUserId && workspaces.data && !workspaces.error && ( |
| 125 | + <Benchmark |
| 126 | + key={`${organizationId}:${runAsUserId}`} |
| 127 | + organizationId={organizationId} |
| 128 | + runAsUserId={runAsUserId} |
| 129 | + canPlan={workspaces.data.pages[0]?.canPlan ?? false} |
| 130 | + /> |
| 131 | + )} |
| 132 | + </main> |
| 133 | + ) |
| 134 | +} |
0 commit comments