Просмотр исходного кода

feat(dashboard): Stock location detail view

Michael Bromley 10 месяцев назад
Родитель
Сommit
8fe7fb38f2

+ 30 - 2
packages/dashboard/src/routes/_authenticated/_stock-locations/stock-locations.graphql.ts

@@ -1,6 +1,6 @@
 import { graphql } from '@/graphql/graphql.js';
 
-export const stockLocationItem = graphql(`
+export const stockLocationFragment = graphql(`
     fragment StockLocationItem on StockLocation {
         id
         createdAt
@@ -21,5 +21,33 @@ export const stockLocationListQuery = graphql(
             }
         }
     `,
-    [stockLocationItem],
+    [stockLocationFragment],
 );
+
+export const stockLocationDetailQuery = graphql(
+    `
+        query StockLocationDetail($id: ID!) {
+            stockLocation(id: $id) {
+                ...StockLocationItem
+                customFields
+            }
+        }
+    `,
+    [stockLocationFragment],
+);
+
+export const createStockLocationDocument = graphql(`
+    mutation CreateStockLocation($input: CreateStockLocationInput!) {
+        createStockLocation(input: $input) {
+            id
+        }
+    }
+`);
+
+export const updateStockLocationDocument = graphql(`
+    mutation UpdateStockLocation($input: UpdateStockLocationInput!) {
+        updateStockLocation(input: $input) {
+            id
+        }
+    }
+`);

+ 151 - 0
packages/dashboard/src/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx

@@ -0,0 +1,151 @@
+import { ErrorPage } from '@/components/shared/error-page.js';
+import { PermissionGuard } from '@/components/shared/permission-guard.js';
+import { Button } from '@/components/ui/button.js';
+import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form.js';
+import { Input } from '@/components/ui/input.js';
+import { Switch } from '@/components/ui/switch.js';
+import { NEW_ENTITY_PATH } from '@/constants.js';
+import { addCustomFields } from '@/framework/document-introspection/add-custom-fields.js';
+import {
+    CustomFieldsPageBlock,
+    Page,
+    PageActionBar,
+    PageBlock,
+    PageLayout,
+    PageTitle,
+} from '@/framework/layout-engine/page-layout.js';
+import { getDetailQueryOptions, useDetailPage } from '@/framework/page/use-detail-page.js';
+import { Trans, useLingui } from '@lingui/react/macro';
+import { createFileRoute, useNavigate } from '@tanstack/react-router';
+import { toast } from 'sonner';
+import {
+    createStockLocationDocument,
+    stockLocationDetailQuery,
+    updateStockLocationDocument,
+} from './stock-locations.graphql.js';
+import { Textarea } from '@/components/ui/textarea.js';
+
+export const Route = createFileRoute('/_authenticated/_stock-locations/stock-locations_/$id')({
+    component: StockLocationDetailPage,
+    loader: async ({ context, params }) => {
+        const isNew = params.id === NEW_ENTITY_PATH;
+        const result = isNew
+            ? null
+            : await context.queryClient.ensureQueryData(
+                  getDetailQueryOptions(addCustomFields(stockLocationDetailQuery), { id: params.id }),
+                  { id: params.id },
+              );
+        if (!isNew && !result.stockLocation) {
+            throw new Error(`Stock location with the ID ${params.id} was not found`);
+        }
+        return {
+            breadcrumb: [
+                { path: '/stock-locations', label: 'Stock locations' },
+                isNew ? <Trans>New stock location</Trans> : result.stockLocation.name,
+            ],
+        };
+    },
+    errorComponent: ({ error }) => <ErrorPage message={error.message} />,
+});
+
+export function StockLocationDetailPage() {
+    const params = Route.useParams();
+    const navigate = useNavigate();
+    const creatingNewEntity = params.id === NEW_ENTITY_PATH;
+    const { i18n } = useLingui();
+
+    const { form, submitHandler, entity, isPending } = useDetailPage({
+        queryDocument: addCustomFields(stockLocationDetailQuery),
+        entityField: 'stockLocation',
+        createDocument: createStockLocationDocument,
+        updateDocument: updateStockLocationDocument,
+        setValuesForUpdate: entity => {
+            return {
+                id: entity.id,
+                name: entity.name,
+                description: entity.description,
+                customFields: entity.customFields,
+            };
+        },
+        params: { id: params.id },
+        onSuccess: async data => {
+            toast(i18n.t('Successfully updated stock location'), {
+                position: 'top-right',
+            });
+            form.reset(form.getValues());
+            if (creatingNewEntity) {
+                await navigate({ to: `../${data?.id}`, from: Route.id });
+            }
+        },
+        onError: err => {
+            toast(i18n.t('Failed to update stock location'), {
+                position: 'top-right',
+                description: err instanceof Error ? err.message : 'Unknown error',
+            });
+        },
+    });
+
+    return (
+        <Page>
+            <PageTitle>
+                {creatingNewEntity ? <Trans>New stock location</Trans> : (entity?.name ?? '')}
+            </PageTitle>
+            <Form {...form}>
+                <form onSubmit={submitHandler} className="space-y-8">
+                    <PageActionBar>
+                        <div></div>
+                        <PermissionGuard requires={['UpdateStockLocation']}>
+                            <Button
+                                type="submit"
+                                disabled={!form.formState.isDirty || !form.formState.isValid || isPending}
+                            >
+                                <Trans>Update</Trans>
+                            </Button>
+                        </PermissionGuard>
+                    </PageActionBar>
+                    <PageLayout>
+                        <PageBlock column="main">
+                            <div className="md:grid md:grid-cols-2 gap-4 mb-4">
+                                <FormField
+                                    control={form.control}
+                                    name="name"
+                                    render={({ field }) => (
+                                        <FormItem>
+                                            <FormLabel>
+                                                <Trans>Name</Trans>
+                                            </FormLabel>
+                                            <FormControl>
+                                                <Input placeholder="" {...field} />
+                                            </FormControl>
+                                            <FormMessage />
+                                        </FormItem>
+                                    )}
+                                />
+                                <div></div>
+                            </div>
+                            <FormField
+                                control={form.control}
+                                name="description"
+                                render={({ field }) => (
+                                    <FormItem>
+                                        <FormLabel>
+                                            <Trans>Description</Trans>
+                                        </FormLabel>
+                                        <FormControl>
+                                            <Textarea {...field} />
+                                        </FormControl>
+                                    </FormItem>
+                                )}
+                            />
+                        </PageBlock>
+                        <CustomFieldsPageBlock
+                            column="main"
+                            entityType="StockLocation"
+                            control={form.control}
+                        />
+                    </PageLayout>
+                </form>
+            </Form>
+        </Page>
+    );
+}