diff --git a/next-env.d.ts b/next-env.d.ts index c4b7818..9edff1c 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/plans/task-fix-display2.md b/plans/task-fix-display2.md new file mode 100644 index 0000000..332b997 --- /dev/null +++ b/plans/task-fix-display2.md @@ -0,0 +1,228 @@ +# Task Fix: Document Sequence Branch Display Name + +## Objective + +Fix `/dashboard/crm/settings/document-sequences` so Branch displays as a readable name/code instead of raw UUID. + +Current issue: + +```txt +1b960bfc-91de-4b69-a389-f060ef21bed2 +``` + +should display as: + +```txt +Branch Name +``` + +or: + +```txt +Branch Code - Branch Name +``` + +--- + +## Problem + +Document Sequence records store: + +```txt +branchId +``` + +but the settings UI currently displays the raw ID directly. + +This breaks admin usability and is inconsistent with the display-name cleanup direction. + +--- + +## Mandatory Review + +Review: + +```txt +src/features/foundation/document-sequence/** +src/app/dashboard/crm/settings/document-sequences/** +src/db/schema.ts +src/features/foundation/display/** +src/features/foundation/master-options/** +``` + +Search: + +```bash +rg "branchId|branch_id|document-sequences|DocumentSequence" src/features/foundation src/app/dashboard/crm/settings/document-sequences +``` + +--- + +## Scope 1: API / DTO Display Fields + +Update document sequence read model to include: + +```ts +branchId?: string | null; +branchCode?: string | null; +branchName?: string | null; +branchDisplayName?: string | null; +``` + +Display rule: + +```txt +branchId = null +→ All Branches + +branchCode + branchName +→ {branchCode} - {branchName} + +branchName only +→ {branchName} + +unknown branch +→ Unknown Branch ({branchId}) +``` + +Do not remove `branchId`; keep it for update/generate logic. + +--- + +## Scope 2: Resolve Branch Display Server-Side + +In document sequence service/list API, resolve branch display before returning response. + +Preferred: + +```txt +Use shared branch display resolver if already created. +``` + +Fallback: + +* If branch is table-backed, join branch table. +* If branch is `ms_options`, resolve from the branch option category. +* If unknown, return fallback display string. + +Avoid N+1 queries. + +Batch resolve all branch IDs in the list. + +--- + +## Scope 3: UI Rendering Fix + +Update document sequence table/cards. + +Replace: + +```tsx +{row.branchId} +``` + +with: + +```tsx +{row.branchDisplayName ?? 'All Branches'} +``` + +or: + +```tsx + +``` + +Rules: + +* never show raw UUID in normal UI +* raw ID may be visible only in debug/dev details if explicitly labelled +* use muted text for fallback unknown branch + +--- + +## Scope 4: Form Select Display + +If create/edit form has branch select: + +* show readable branch names in dropdown +* value remains `branchId` +* include option: + +```txt +All Branches +``` + +for nullable branch + +Dropdown labels: + +```txt +ALL - All Branches +BKK - Bangkok +RYG - Rayong +``` + +--- + +## Scope 5: Seed Check + +Verify seed creates branch data that can be resolved. + +Check whether branch source is: + +```txt +branch table +``` + +or: + +```txt +ms_options category +``` + +If document sequence references a branch ID that seed does not create, fix seed. + +--- + +## Scope 6: Verification + +Run: + +```bash +npm exec tsc --noEmit +npm run build +``` + +Manual verification: + +```txt +Open /dashboard/crm/settings/document-sequences +No raw UUID is shown for branch +All Branches displays correctly for null branchId +Known branch displays readable name/code +Unknown branch fallback is readable +Create/edit branch dropdown shows names, not IDs +Saving still persists branchId correctly +Document number generation still works +``` + +--- + +## Definition of Done + +Task is complete when: + +* Document Sequence list does not show raw branch UUID +* API returns branch display fields +* UI uses branch display fields +* branch select uses readable labels +* document sequence generation behavior remains unchanged + +Result: + +```txt +Document Sequence Branch Display = Fixed +Admin UX = Improved +Raw Branch UUID Leakage = Removed +``` diff --git a/src/app/dashboard/crm/settings/document-sequences/page.tsx b/src/app/dashboard/crm/settings/document-sequences/page.tsx index cdb248e..78820c7 100644 --- a/src/app/dashboard/crm/settings/document-sequences/page.tsx +++ b/src/app/dashboard/crm/settings/document-sequences/page.tsx @@ -1,6 +1,7 @@ import { auth } from '@/auth'; import { HydrationBoundary, dehydrate } from '@tanstack/react-query'; import PageContainer from '@/components/layout/page-container'; +import { getUserBranches } from '@/features/foundation/branch-scope/service'; import { DocumentSequenceSettings } from '@/features/foundation/document-sequence/components/document-sequence-settings'; import { documentSequencesQueryOptions } from '@/features/foundation/document-sequence/queries'; import { PERMISSIONS } from '@/lib/auth/rbac'; @@ -19,6 +20,11 @@ export default async function DocumentSequencesRoute() { void queryClient.prefetchQuery(documentSequencesQueryOptions()); } + const branches = + canRead && session?.user?.activeOrganizationId + ? await getUserBranches(session.user.activeOrganizationId) + : []; + return ( {canRead ? ( - + ) : null} diff --git a/src/app/dashboard/crm/settings/master-options/page.tsx b/src/app/dashboard/crm/settings/master-options/page.tsx index abb89a8..4b7d0e1 100644 --- a/src/app/dashboard/crm/settings/master-options/page.tsx +++ b/src/app/dashboard/crm/settings/master-options/page.tsx @@ -1,5 +1,6 @@ import { auth } from '@/auth'; import PageContainer from '@/components/layout/page-container'; +import { MasterOptionsCreateAction } from '@/features/foundation/master-options/components/master-options-create-action'; import MasterOptionsListingPage from '@/features/foundation/master-options/components/master-options-listing'; import { PERMISSIONS } from '@/lib/auth/rbac'; import { searchParamsCache } from '@/lib/searchparams'; @@ -12,11 +13,16 @@ type PageProps = { export default async function MasterOptionsRoute(props: PageProps) { const searchParams = await props.searchParams; const session = await auth(); - const canManageOptions = + const canReadOptions = session?.user?.systemRole === 'super_admin' || (!!session?.user?.activeOrganizationId && (session.user.activeMembershipRole === 'admin' || session.user.activePermissions.includes(PERMISSIONS.crmMasterOptionRead))); + const canCreateOptions = + session?.user?.systemRole === 'super_admin' || + (!!session?.user?.activeOrganizationId && + (session.user.activeMembershipRole === 'admin' || + session.user.activePermissions.includes(PERMISSIONS.crmMasterOptionCreate))); searchParamsCache.parse(searchParams); @@ -24,12 +30,13 @@ export default async function MasterOptionsRoute(props: PageProps) { You do not have access to master option management. } + pageHeaderAction={canCreateOptions ? : null} > diff --git a/src/features/foundation/document-sequence/components/document-sequence-settings.tsx b/src/features/foundation/document-sequence/components/document-sequence-settings.tsx index e7bf244..c91088e 100644 --- a/src/features/foundation/document-sequence/components/document-sequence-settings.tsx +++ b/src/features/foundation/document-sequence/components/document-sequence-settings.tsx @@ -15,8 +15,16 @@ import { DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { formatDateTime } from '@/lib/date-format'; +import type { FoundationBranch } from '@/features/foundation/branch-scope/types'; import { createDocumentSequenceMutation, deleteDocumentSequenceMutation, @@ -24,7 +32,11 @@ import { updateDocumentSequenceMutation } from '../mutations'; import { documentSequencesQueryOptions } from '../queries'; -import type { DocumentSequenceListItem, DocumentSequenceMutationPayload } from '../types'; +import type { + DocumentSequenceBranchOption, + DocumentSequenceListItem, + DocumentSequenceMutationPayload +} from '../types'; type SequenceState = { documentType: string; @@ -36,12 +48,21 @@ type SequenceState = { isActive: boolean; }; +function toBranchOptions(branches: FoundationBranch[]): DocumentSequenceBranchOption[] { + return branches.map((branch) => ({ + id: branch.id, + code: branch.code, + name: branch.name, + displayName: `${branch.code.toUpperCase()} - ${branch.name}` + })); +} + function toState(sequence?: DocumentSequenceListItem): SequenceState { return { documentType: sequence?.documentType ?? 'quotation', prefix: sequence?.prefix ?? 'QT', period: sequence?.period ?? '', - branchId: sequence?.branchId ?? '', + branchId: sequence?.branchId ?? '__all__', currentNumber: String(sequence?.currentNumber ?? 0), paddingLength: String(sequence?.paddingLength ?? 3), isActive: sequence?.isActive ?? true @@ -53,7 +74,7 @@ function buildPayload(state: SequenceState): DocumentSequenceMutationPayload { documentType: state.documentType, prefix: state.prefix, period: state.period, - branchId: state.branchId || null, + branchId: state.branchId === '__all__' ? null : state.branchId, currentNumber: Number(state.currentNumber || 0), paddingLength: Number(state.paddingLength || 3), isActive: state.isActive @@ -63,11 +84,13 @@ function buildPayload(state: SequenceState): DocumentSequenceMutationPayload { function SequenceDialog({ open, onOpenChange, - sequence + sequence, + branches }: { open: boolean; onOpenChange: (open: boolean) => void; sequence?: DocumentSequenceListItem; + branches: DocumentSequenceBranchOption[]; }) { const [state, setState] = React.useState(toState(sequence)); const createMutation = useMutation({ @@ -76,7 +99,9 @@ function SequenceDialog({ toast.success('Sequence created'); onOpenChange(false); }, - onError: (error) => toast.error(error instanceof Error ? error.message : 'Create failed') + onError: (error) => { + toast.error(error instanceof Error ? error.message : 'Create failed'); + } }); const updateMutation = useMutation({ ...updateDocumentSequenceMutation, @@ -84,7 +109,9 @@ function SequenceDialog({ toast.success('Sequence updated'); onOpenChange(false); }, - onError: (error) => toast.error(error instanceof Error ? error.message : 'Update failed') + onError: (error) => { + toast.error(error instanceof Error ? error.message : 'Update failed'); + } }); React.useEffect(() => { @@ -120,38 +147,57 @@ function SequenceDialog({ setState((current) => ({ ...current, documentType: e.target.value }))} + onChange={(event) => + setState((current) => ({ ...current, documentType: event.target.value })) + } />
setState((current) => ({ ...current, prefix: e.target.value }))} + onChange={(event) => + setState((current) => ({ ...current, prefix: event.target.value })) + } /> setState((current) => ({ ...current, period: e.target.value }))} + onChange={(event) => + setState((current) => ({ ...current, period: event.target.value })) + } />
- setState((current) => ({ ...current, branchId: e.target.value }))} - /> + onValueChange={(value) => + setState((current) => ({ ...current, branchId: value })) + } + > + + + + + ALL - All Branches + {branches.map((branch) => ( + + {branch.displayName} + + ))} + + - setState((current) => ({ ...current, currentNumber: e.target.value })) + onChange={(event) => + setState((current) => ({ ...current, currentNumber: event.target.value })) } /> - setState((current) => ({ ...current, paddingLength: e.target.value })) + onChange={(event) => + setState((current) => ({ ...current, paddingLength: event.target.value })) } />
@@ -183,7 +229,7 @@ function SequenceDialog({ ); } -export function DocumentSequenceSettings() { +export function DocumentSequenceSettings({ branches }: { branches: FoundationBranch[] }) { const { data } = useSuspenseQuery(documentSequencesQueryOptions()); const [dialogState, setDialogState] = React.useState<{ open: boolean; @@ -191,15 +237,25 @@ export function DocumentSequenceSettings() { }>({ open: false }); + const branchOptions = React.useMemo(() => toBranchOptions(branches), [branches]); + const resetMutation = useMutation({ ...resetDocumentSequenceMutation, - onSuccess: () => toast.success('Sequence reset'), - onError: (error) => toast.error(error instanceof Error ? error.message : 'Reset failed') + onSuccess: () => { + toast.success('Sequence reset'); + }, + onError: (error) => { + toast.error(error instanceof Error ? error.message : 'Reset failed'); + } }); const deleteMutation = useMutation({ ...deleteDocumentSequenceMutation, - onSuccess: () => toast.success('Sequence deleted'), - onError: (error) => toast.error(error instanceof Error ? error.message : 'Delete failed') + onSuccess: () => { + toast.success('Sequence deleted'); + }, + onError: (error) => { + toast.error(error instanceof Error ? error.message : 'Delete failed'); + } }); return ( @@ -229,8 +285,16 @@ export function DocumentSequenceSettings() {
{sequence.documentType}
- {sequence.prefix} - {sequence.period} • branch {sequence.branchId || 'default'} + {sequence.prefix} {sequence.period} {'\u2022'}{' '} + + {sequence.branchDisplayName ?? 'All Branches'} +
@@ -240,7 +304,14 @@ export function DocumentSequenceSettings() { Next {sequence.nextPreview}
+
+
+
Branch
+
+ {sequence.branchDisplayName ?? 'All Branches'} +
+
Current Number
{sequence.currentNumber}
@@ -251,17 +322,15 @@ export function DocumentSequenceSettings() {
Updated At
-
- {formatDateTime(sequence.updatedAt)} -
-
-
-
Preview
-
{sequence.nextPreview}
+
{formatDateTime(sequence.updatedAt)}
+
-
); diff --git a/src/features/foundation/document-sequence/service.ts b/src/features/foundation/document-sequence/service.ts index fb3cf39..bf352d2 100644 --- a/src/features/foundation/document-sequence/service.ts +++ b/src/features/foundation/document-sequence/service.ts @@ -1,8 +1,9 @@ import { and, asc, eq, sql } from 'drizzle-orm'; import { documentSequences } from '@/db/schema'; +import { resolveBranchDisplays } from '@/features/foundation/display/server/display-resolver'; +import { getCurrentOrganization } from '@/features/foundation/organization-context/service'; import { db } from '@/lib/db'; import { AuthError } from '@/lib/auth/session'; -import { getCurrentOrganization } from '@/features/foundation/organization-context/service'; import type { DocumentSequenceInput, DocumentSequenceListItem, @@ -42,32 +43,24 @@ function normalizeBranchId(branchId?: string | null) { return branchId?.trim() || ''; } -function mapSequenceRecord(row: typeof documentSequences.$inferSelect): DocumentSequenceRecord { - return { - id: row.id, - organizationId: row.organizationId, - branchId: row.branchId || null, - documentType: row.documentType, - prefix: row.prefix, - period: row.period, - currentNumber: row.currentNumber, - paddingLength: row.paddingLength, - isActive: row.isActive, - createdAt: row.createdAt.toISOString(), - updatedAt: row.updatedAt.toISOString() - }; -} +function formatBranchDisplayName(input: { + branchId: string | null; + branchCode?: string | null; + branchName?: string | null; +}) { + if (!input.branchId) { + return 'All Branches'; + } -function mapSequenceListItem(row: typeof documentSequences.$inferSelect): DocumentSequenceListItem { - return { - ...mapSequenceRecord(row), - nextPreview: buildDocumentCode( - row.prefix, - row.period, - row.currentNumber + 1, - row.paddingLength - ) - }; + if (input.branchCode && input.branchName) { + return `${input.branchCode} - ${input.branchName}`; + } + + if (input.branchName) { + return input.branchName; + } + + return `Unknown Branch (${input.branchId})`; } async function resolveOrganizationId(organizationId?: string) { @@ -76,31 +69,43 @@ async function resolveOrganizationId(organizationId?: string) { } const organization = await getCurrentOrganization(); - if (!organization) { - throw new Error('Active organization is required'); + throw new AuthError('Active organization required', 400); } return organization.id; } -async function assertSequence(id: string, organizationId: string) { - const [sequence] = await db - .select() - .from(documentSequences) - .where( - and(eq(documentSequences.id, id), eq(documentSequences.organizationId, organizationId)) - ) - .limit(1); +function toSequenceResult(row: typeof documentSequences.$inferSelect): DocumentSequenceResult { + const nextNumber = row.currentNumber + 1; - if (!sequence) { + return { + code: buildDocumentCode(row.prefix, row.period, nextNumber, row.paddingLength), + documentType: row.documentType, + branchId: row.branchId || null, + currentNumber: row.currentNumber, + nextNumber, + period: row.period, + prefix: row.prefix + }; +} + +async function assertSequence(id: string, organizationId: string) { + const row = await db.query.documentSequences.findFirst({ + where: and( + eq(documentSequences.id, id), + eq(documentSequences.organizationId, organizationId) + ) + }); + + if (!row) { throw new AuthError('Document sequence not found', 404); } - return sequence; + return row; } -async function ensureSequence( +async function ensureSequenceRow( organizationId: string, documentType: string, period: string, @@ -119,7 +124,8 @@ async function ensureSequence( return existing; } - const prefix = DEFAULT_DOCUMENT_PREFIXES[documentType] ?? documentType.slice(0, 3).toUpperCase(); + const prefix = + DEFAULT_DOCUMENT_PREFIXES[documentType] ?? documentType.slice(0, 3).toUpperCase(); const [created] = await db .insert(documentSequences) @@ -139,21 +145,45 @@ async function ensureSequence( return created; } -function toSequenceResult(row: typeof documentSequences.$inferSelect): DocumentSequenceResult { - const nextNumber = row.currentNumber + 1; +async function mapSequenceRecords( + organizationId: string, + rows: Array +) { + const branchDisplayMap = await resolveBranchDisplays({ + organizationId, + branchIds: rows.map((row) => row.branchId || null) + }); - return { - code: buildDocumentCode(row.prefix, row.period, nextNumber, row.paddingLength), - documentType: row.documentType, - branchId: row.branchId || null, - currentNumber: row.currentNumber, - nextNumber, - period: row.period, - prefix: row.prefix - }; + return rows.map((row) => { + const branchId = row.branchId || null; + const branchDisplay = branchId ? (branchDisplayMap.get(branchId) ?? null) : null; + + return { + id: row.id, + organizationId: row.organizationId, + branchId, + branchCode: branchDisplay?.code ?? null, + branchName: branchDisplay?.name ?? null, + branchDisplayName: formatBranchDisplayName({ + branchId, + branchCode: branchDisplay?.code ?? null, + branchName: branchDisplay?.name ?? null + }), + documentType: row.documentType, + prefix: row.prefix, + period: row.period, + currentNumber: row.currentNumber, + paddingLength: row.paddingLength, + isActive: row.isActive, + createdAt: row.createdAt.toISOString(), + updatedAt: row.updatedAt.toISOString() + } satisfies DocumentSequenceRecord; + }); } -export async function listDocumentSequences(organizationId: string): Promise { +export async function listDocumentSequences( + organizationId: string +): Promise { const rows = await db .select() .from(documentSequences) @@ -164,12 +194,27 @@ export async function listDocumentSequences(organizationId: string): Promise ({ + ...record, + nextPreview: buildDocumentCode( + record.prefix, + record.period, + record.currentNumber + 1, + record.paddingLength + ) + })); } -export async function getDocumentSequence(id: string, organizationId: string): Promise { +export async function getDocumentSequence( + id: string, + organizationId: string +): Promise { const row = await assertSequence(id, organizationId); - return mapSequenceRecord(row); + const [record] = await mapSequenceRecords(organizationId, [row]); + + return record; } export async function createDocumentSequence( @@ -193,7 +238,8 @@ export async function createDocumentSequence( }) .returning(); - return mapSequenceRecord(created); + const [record] = await mapSequenceRecords(organizationId, [created]); + return record; } export async function updateDocumentSequence( @@ -209,7 +255,9 @@ export async function updateDocumentSequence( prefix: payload.prefix?.trim() ?? current.prefix, period: payload.period?.trim() ?? current.period, branchId: - payload.branchId === undefined ? current.branchId : normalizeBranchId(payload.branchId), + payload.branchId === undefined + ? current.branchId + : normalizeBranchId(payload.branchId), currentNumber: payload.currentNumber ?? current.currentNumber, paddingLength: payload.paddingLength ?? current.paddingLength, isActive: payload.isActive ?? current.isActive, @@ -218,7 +266,8 @@ export async function updateDocumentSequence( .where(eq(documentSequences.id, id)) .returning(); - return mapSequenceRecord(updated); + const [record] = await mapSequenceRecords(organizationId, [updated]); + return record; } export async function resetDocumentSequence( @@ -236,75 +285,40 @@ export async function resetDocumentSequence( .where(eq(documentSequences.id, id)) .returning(); - return mapSequenceRecord(updated); + const [record] = await mapSequenceRecords(organizationId, [updated]); + return record; } -export async function deleteDocumentSequence(id: string, organizationId: string): Promise { - await assertSequence(id, organizationId); - const [deleted] = await db - .delete(documentSequences) - .where(eq(documentSequences.id, id)) - .returning(); - - return mapSequenceRecord(deleted); -} - -export async function previewDocumentSequenceById( - id: string, - organizationId: string -): Promise { +export async function deleteDocumentSequence(id: string, organizationId: string) { const sequence = await assertSequence(id, organizationId); - return toSequenceResult(sequence); + await db.delete(documentSequences).where(eq(documentSequences.id, id)); + + const [record] = await mapSequenceRecords(organizationId, [sequence]); + return record; } -export async function previewNextDocumentCode( - input: DocumentSequenceInput -): Promise { +export async function previewDocumentSequenceById(id: string, organizationId: string) { + const row = await assertSequence(id, organizationId); + return toSequenceResult(row); +} + +export async function generateNextDocumentCode(input: DocumentSequenceInput) { const organizationId = await resolveOrganizationId(input.organizationId); const period = input.period ?? getCurrentPeriod(); const branchId = normalizeBranchId(input.branchId); - const sequence = await ensureSequence(organizationId, input.documentType, period, branchId); - - return toSequenceResult(sequence); -} - -export async function generateNextDocumentCode( - input: DocumentSequenceInput -): Promise { - const organizationId = await resolveOrganizationId(input.organizationId); - const period = input.period ?? getCurrentPeriod(); - const branchId = normalizeBranchId(input.branchId); - - await ensureSequence(organizationId, input.documentType, period, branchId); return db.transaction(async (tx) => { - await tx.execute(sql` - select id - from document_sequences - where organization_id = ${organizationId} - and document_type = ${input.documentType} - and period = ${period} - and branch_id = ${branchId} - for update - `); - - const sequence = await tx.query.documentSequences.findFirst({ - where: and( - eq(documentSequences.organizationId, organizationId), - eq(documentSequences.documentType, input.documentType), - eq(documentSequences.period, period), - eq(documentSequences.branchId, branchId) - ) - }); - - if (!sequence) { - throw new Error('Document sequence not found'); - } + const sequence = await ensureSequenceRow( + organizationId, + input.documentType.trim(), + period, + branchId + ); const [updated] = await tx .update(documentSequences) .set({ - currentNumber: sequence.currentNumber + 1, + currentNumber: sql`${documentSequences.currentNumber} + 1`, updatedAt: new Date() }) .where(eq(documentSequences.id, sequence.id)) @@ -323,7 +337,6 @@ export async function generateNextDocumentCode( nextNumber: updated.currentNumber + 1, period: updated.period, prefix: updated.prefix - }; + } satisfies DocumentSequenceResult; }); } - diff --git a/src/features/foundation/document-sequence/types.ts b/src/features/foundation/document-sequence/types.ts index 160368f..2c07885 100644 --- a/src/features/foundation/document-sequence/types.ts +++ b/src/features/foundation/document-sequence/types.ts @@ -9,6 +9,9 @@ export interface DocumentSequenceRecord { id: string; organizationId: string; branchId: string | null; + branchCode: string | null; + branchName: string | null; + branchDisplayName: string | null; documentType: string; prefix: string; period: string; @@ -33,6 +36,13 @@ export interface DocumentSequenceListItem extends DocumentSequenceRecord { nextPreview: string; } +export interface DocumentSequenceBranchOption { + id: string; + code: string; + name: string; + displayName: string; +} + export interface DocumentSequenceListResponse { success: boolean; time: string; diff --git a/src/features/foundation/master-options/components/master-options-create-action.tsx b/src/features/foundation/master-options/components/master-options-create-action.tsx new file mode 100644 index 0000000..46adbc1 --- /dev/null +++ b/src/features/foundation/master-options/components/master-options-create-action.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { useState } from 'react'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { Icons } from '@/components/icons'; +import { Button } from '@/components/ui/button'; +import { createMasterOptionMutation } from '../api/mutations'; +import { masterOptionsQueryOptions } from '../api/queries'; +import type { MasterOptionMutationPayload } from '../api/types'; +import { MasterOptionsFormDialog } from './master-options-form-dialog'; + +export function MasterOptionsCreateAction() { + const [open, setOpen] = useState(false); + const { data } = useQuery(masterOptionsQueryOptions({ page: 1, limit: 200 })); + const createMutation = useMutation({ + ...createMasterOptionMutation, + onSuccess: () => { + toast.success('Master option created.'); + setOpen(false); + }, + onError: (error) => { + toast.error(error instanceof Error ? error.message : 'Create failed'); + } + }); + + async function handleSubmit(values: MasterOptionMutationPayload) { + await createMutation.mutateAsync(values); + } + + return ( + <> + + + + ); +} diff --git a/src/features/foundation/master-options/components/master-options-table.tsx b/src/features/foundation/master-options/components/master-options-table.tsx index 6716772..563bc4c 100644 --- a/src/features/foundation/master-options/components/master-options-table.tsx +++ b/src/features/foundation/master-options/components/master-options-table.tsx @@ -138,18 +138,7 @@ export function MasterOptionsTable() { return ( -
- - -
+