task-d.7.6
This commit is contained in:
27
src/app/dashboard/administration/setup/page.tsx
Normal file
27
src/app/dashboard/administration/setup/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { auth } from '@/auth';
|
||||
import PageContainer from '@/components/layout/page-container';
|
||||
import { SetupWizard } from '@/features/setup/components/setup-wizard';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Dashboard: Administration Setup'
|
||||
};
|
||||
|
||||
export default async function AdministrationSetupPage() {
|
||||
const session = await auth();
|
||||
const canAccessSetup = session?.user?.systemRole === 'super_admin';
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
pageTitle='Administration Setup Wizard'
|
||||
pageDescription='Run readiness checks, review setup templates, preview CSV imports, commit imports, and verify the configured system.'
|
||||
access={canAccessSetup}
|
||||
accessFallback={
|
||||
<div className='rounded-lg border border-dashed p-8 text-center text-muted-foreground'>
|
||||
Setup administration requires a super admin account.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<SetupWizard />
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
@@ -170,17 +170,25 @@ import { NavGroup } from "@/types";
|
||||
// shortcut: ["d", "d"],
|
||||
// items: [],
|
||||
// },
|
||||
{
|
||||
title: "Workspaces",
|
||||
url: "/dashboard/workspaces",
|
||||
icon: "workspace",
|
||||
isActive: false,
|
||||
{
|
||||
title: "Workspaces",
|
||||
url: "/dashboard/workspaces",
|
||||
icon: "workspace",
|
||||
isActive: false,
|
||||
items: [],
|
||||
access: { systemRole: "super_admin" },
|
||||
},
|
||||
{
|
||||
title: "Teams",
|
||||
url: "/dashboard/workspaces/team",
|
||||
access: { systemRole: "super_admin" },
|
||||
},
|
||||
{
|
||||
title: "Setup Wizard",
|
||||
url: "/dashboard/administration/setup",
|
||||
icon: "settings",
|
||||
isActive: false,
|
||||
items: [],
|
||||
access: { systemRole: "super_admin" },
|
||||
},
|
||||
{
|
||||
title: "Teams",
|
||||
url: "/dashboard/workspaces/team",
|
||||
icon: "teams",
|
||||
isActive: false,
|
||||
items: [],
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
import { getSetupReadiness, verifySetup } from './service';
|
||||
import { getSetupReadiness, getSetupTemplates, verifySetup } from './service';
|
||||
import type { SetupVerificationPayload } from './types';
|
||||
|
||||
export const setupKeys = {
|
||||
all: ['setup'] as const,
|
||||
readiness: () => [...setupKeys.all, 'readiness'] as const,
|
||||
templates: () => [...setupKeys.all, 'templates'] as const,
|
||||
verification: (payload: SetupVerificationPayload = {}) =>
|
||||
[...setupKeys.all, 'verification', payload] as const
|
||||
};
|
||||
@@ -15,6 +16,12 @@ export const setupReadinessQueryOptions = () =>
|
||||
queryFn: () => getSetupReadiness()
|
||||
});
|
||||
|
||||
export const setupTemplatesQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: setupKeys.templates(),
|
||||
queryFn: () => getSetupTemplates()
|
||||
});
|
||||
|
||||
export const setupVerificationQueryOptions = (payload: SetupVerificationPayload = {}) =>
|
||||
queryOptions({
|
||||
queryKey: setupKeys.verification(payload),
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import { apiClient } from '@/lib/api-client';
|
||||
import { ApiClientError, apiClient } from '@/lib/api-client';
|
||||
import type {
|
||||
CsvPreviewResult,
|
||||
ImportReport,
|
||||
SetupReadinessReport,
|
||||
SetupTemplatesResponse,
|
||||
SetupVerificationPayload,
|
||||
SetupVerificationReport
|
||||
} from './types';
|
||||
|
||||
interface ApiErrorPayload {
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export async function getSetupReadiness(): Promise<SetupReadinessReport> {
|
||||
return apiClient<SetupReadinessReport>('/setup/readiness');
|
||||
}
|
||||
|
||||
export async function getSetupTemplates(): Promise<SetupTemplatesResponse> {
|
||||
return apiClient<SetupTemplatesResponse>('/setup/templates');
|
||||
}
|
||||
|
||||
export async function verifySetup(
|
||||
payload: SetupVerificationPayload = {}
|
||||
): Promise<SetupVerificationReport> {
|
||||
@@ -17,3 +29,50 @@ export async function verifySetup(
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
async function readUploadResponse<T>(response: Response): Promise<T> {
|
||||
if (response.ok) {
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
const payload = (await response.json().catch(() => null)) as ApiErrorPayload | null;
|
||||
const message = payload?.message ?? payload?.error ?? `API error: ${response.status}`;
|
||||
|
||||
throw new ApiClientError(message, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
payload
|
||||
});
|
||||
}
|
||||
|
||||
function buildCsvFormData(files: File[]): FormData {
|
||||
const formData = new FormData();
|
||||
files.forEach((file) => formData.append('files', file, file.name));
|
||||
return formData;
|
||||
}
|
||||
|
||||
export async function previewSetupCsvFiles(files: File[]): Promise<CsvPreviewResult> {
|
||||
return readUploadResponse<CsvPreviewResult>(
|
||||
await fetch('/api/setup/import/preview', {
|
||||
method: 'POST',
|
||||
body: buildCsvFormData(files)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export async function commitSetupCsvFiles(input: {
|
||||
files: File[];
|
||||
previewHash: string;
|
||||
dryRun?: boolean;
|
||||
}): Promise<ImportReport> {
|
||||
const formData = buildCsvFormData(input.files);
|
||||
formData.set('previewHash', input.previewHash);
|
||||
formData.set('dryRun', String(Boolean(input.dryRun)));
|
||||
|
||||
return readUploadResponse<ImportReport>(
|
||||
await fetch('/api/setup/import/commit', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,3 +37,36 @@ export interface SetupVerificationPayload {
|
||||
export interface SetupVerificationReport extends SetupReadinessReport {
|
||||
organizationId: string | null;
|
||||
}
|
||||
|
||||
export interface SetupTemplateMetadataItem {
|
||||
file: string;
|
||||
seedType: string;
|
||||
importOrder: number;
|
||||
supported: boolean;
|
||||
requiredColumns: string[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SetupTemplatesResponse {
|
||||
success: boolean;
|
||||
time: string;
|
||||
version: string;
|
||||
generatedFrom: string;
|
||||
templates: SetupTemplateMetadataItem[];
|
||||
}
|
||||
|
||||
export type {
|
||||
CsvPreviewError,
|
||||
CsvPreviewFileResult,
|
||||
CsvPreviewRow,
|
||||
CsvPreviewResult,
|
||||
CsvPreviewStatus,
|
||||
CsvPreviewWarning
|
||||
} from '@/features/setup/server/csv/types';
|
||||
|
||||
export type {
|
||||
ImportFileReport,
|
||||
ImportReport,
|
||||
ImportRowReport,
|
||||
ImportStatus
|
||||
} from '@/features/setup/server/csv/import-report';
|
||||
|
||||
17
src/features/setup/api/use-csv-commit.ts
Normal file
17
src/features/setup/api/use-csv-commit.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { commitSetupCsvFiles, previewSetupCsvFiles } from './service';
|
||||
|
||||
export function useCsvPreview() {
|
||||
return useMutation({
|
||||
mutationFn: (files: File[]) => previewSetupCsvFiles(files)
|
||||
});
|
||||
}
|
||||
|
||||
export function useCsvCommit() {
|
||||
return useMutation({
|
||||
mutationFn: (input: { files: File[]; previewHash: string; dryRun?: boolean }) =>
|
||||
commitSetupCsvFiles(input)
|
||||
});
|
||||
}
|
||||
11
src/features/setup/api/use-setup-verification.ts
Normal file
11
src/features/setup/api/use-setup-verification.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { verifySetup } from './service';
|
||||
import type { SetupVerificationPayload } from './types';
|
||||
|
||||
export function useSetupVerification() {
|
||||
return useMutation({
|
||||
mutationFn: (payload: SetupVerificationPayload = {}) => verifySetup(payload)
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user