Browse Source

feat(dashboard): Support for custom fields in detail page component (#3599)

David Höck 7 months ago
parent
commit
32314cc5d3

+ 57 - 38
packages/dashboard/src/lib/framework/page/detail-page.tsx

@@ -1,18 +1,19 @@
+import { DateTimeInput } from '@/components/data-input/datetime-input.js';
 import { FormFieldWrapper } from '@/components/shared/form-field-wrapper.js';
+import { Button } from '@/components/ui/button.js';
+import { Checkbox } from '@/components/ui/checkbox.js';
 import { Input } from '@/components/ui/input.js';
+import { NEW_ENTITY_PATH } from '@/constants.js';
 import { useDetailPage } from '@/framework/page/use-detail-page.js';
 import { Trans } from '@/lib/trans.js';
 import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
 import { AnyRoute, useNavigate } from '@tanstack/react-router';
 import { ResultOf, VariablesOf } from 'gql.tada';
-import { DateTimeInput } from '@/components/data-input/datetime-input.js';
-import { Button } from '@/components/ui/button.js';
-import { Checkbox } from '@/components/ui/checkbox.js';
-import { NEW_ENTITY_PATH } from '@/constants.js';
 import { toast } from 'sonner';
 import { getOperationVariablesFields } from '../document-introspection/get-document-structure.js';
 
 import {
+    CustomFieldsPageBlock,
     DetailFormGrid,
     Page,
     PageActionBar,
@@ -37,6 +38,11 @@ export interface DetailPageProps<
     U extends TypedDocumentNode<any, any>,
     EntityField extends keyof ResultOf<T> = DetailEntityPath<T>,
 > {
+    /**
+     * @description
+     * The name of the entity.
+     */
+    entityName?: string;
     /**
      * @description
      * A unique identifier for the page.
@@ -94,6 +100,7 @@ export function DetailPage<
 >({
     pageId,
     route,
+    entityName,
     queryDocument,
     createDocument,
     updateDocument,
@@ -110,7 +117,7 @@ export function DetailPage<
         createDocument,
         params: { id: params.id },
         setValuesForUpdate,
-        onSuccess: async (data) => {
+        onSuccess: async data => {
             toast.success('Updated successfully');
             resetForm();
             const id = (data as any).id;
@@ -143,41 +150,53 @@ export function DetailPage<
             <PageLayout>
                 <PageBlock column="main" blockId="main-form">
                     <DetailFormGrid>
-                        {updateFields.map(fieldInfo => {
-                            if (fieldInfo.name === 'id' && fieldInfo.type === 'ID') {
-                                return null;
-                            }
-                            return (
-                                <FormFieldWrapper
-                                    key={fieldInfo.name}
-                                    control={form.control}
-                                    name={fieldInfo.name as never}
-                                    label={fieldInfo.name}
-                                    render={({ field }) => {
-                                        switch (fieldInfo.type) {
-                                            case 'Int':
-                                            case 'Float':
-                                                return (
-                                                    <Input
-                                                        type="number"
-                                                        value={field.value}
-                                                        onChange={e => field.onChange(e.target.valueAsNumber)}
-                                                    />
-                                                );
-                                            case 'DateTime':
-                                                return <DateTimeInput {...field} />;
-                                            case 'Boolean':
-                                                return <Checkbox value={field.value} onCheckedChange={field.onChange} />;
-                                            case 'String':
-                                            default:
-                                                return <Input {...field} />;
-                                        }
-                                    }}
-                                />
-                            );
-                        })}
+                        {updateFields
+                            .filter(fieldInfo => fieldInfo.name !== 'customFields')
+                            .map(fieldInfo => {
+                                if (fieldInfo.name === 'id' && fieldInfo.type === 'ID') {
+                                    return null;
+                                }
+                                return (
+                                    <FormFieldWrapper
+                                        key={fieldInfo.name}
+                                        control={form.control}
+                                        name={fieldInfo.name as never}
+                                        label={fieldInfo.name}
+                                        render={({ field }) => {
+                                            switch (fieldInfo.type) {
+                                                case 'Int':
+                                                case 'Float':
+                                                    return (
+                                                        <Input
+                                                            type="number"
+                                                            value={field.value}
+                                                            onChange={e =>
+                                                                field.onChange(e.target.valueAsNumber)
+                                                            }
+                                                        />
+                                                    );
+                                                case 'DateTime':
+                                                    return <DateTimeInput {...field} />;
+                                                case 'Boolean':
+                                                    return (
+                                                        <Checkbox
+                                                            value={field.value}
+                                                            onCheckedChange={field.onChange}
+                                                        />
+                                                    );
+                                                case 'String':
+                                                default:
+                                                    return <Input {...field} />;
+                                            }
+                                        }}
+                                    />
+                                );
+                            })}
                     </DetailFormGrid>
                 </PageBlock>
+                {entityName && (
+                    <CustomFieldsPageBlock column="main" entityType={entityName} control={form.control} />
+                )}
             </PageLayout>
         </Page>
     );

+ 5 - 5
packages/dev-server/test-plugins/reviews/dashboard/review-detail.tsx

@@ -1,9 +1,5 @@
 import { graphql } from '@/graphql/graphql';
-import {
-    DashboardRouteDefinition,
-    DetailPage,
-    detailPageRouteLoader
-} from '@vendure/dashboard';
+import { DashboardRouteDefinition, DetailPage, detailPageRouteLoader } from '@vendure/dashboard';
 
 const reviewDetailDocument = graphql(`
     query GetReviewDetail($id: ID!) {
@@ -30,6 +26,9 @@ const reviewDetailDocument = graphql(`
             state
             response
             responseCreatedAt
+            customFields {
+                reviewerName
+            }
         }
     }
 `);
@@ -54,6 +53,7 @@ export const reviewDetail: DashboardRouteDefinition = {
     component: route => {
         return (
             <DetailPage
+                entityName="ProductReview"
                 pageId="review-detail"
                 queryDocument={reviewDetailDocument}
                 updateDocument={updateReviewDocument}

+ 6 - 0
packages/dev-server/test-plugins/reviews/dashboard/review-list.tsx

@@ -65,6 +65,12 @@ export const reviewList: DashboardRouteDefinition = {
                 downvotes: false,
             }}
             customizeColumns={{
+                id: {
+                    header: 'ID',
+                    cell: ({ row }) => {
+                        return <DetailPageButton id={row.original.id} label={row.original.id} />;
+                    },
+                },
                 product: {
                     header: 'Product',
                     cell: ({ row }) => {