Преглед изворни кода

feat(dashboard): List page initial implementation

Michael Bromley пре 11 месеци
родитељ
комит
fce2e66cd5

+ 13 - 0
packages/dashboard/src/framework/internal/document-introspection/get-document-structure.ts

@@ -48,6 +48,19 @@ export function getListQueryFields(documentNode: DocumentNode): FieldInfo[] {
     return fields;
 }
 
+export function getQueryName(documentNode: DocumentNode): string {
+    const operationDefinition = documentNode.definitions.find(
+        (def): def is OperationDefinitionNode =>
+            def.kind === 'OperationDefinition' && def.operation === 'query',
+    );
+    const firstSelection = operationDefinition?.selectionSet.selections[0];
+    if (firstSelection?.kind === 'Field') {
+        return firstSelection.name.value;
+    } else {
+        throw new Error('Could not determine query name');
+    }
+}
+
 function getQueryInfo(name: string): FieldInfo {
     const fieldInfo = schemaInfo.types.Query[name];
     return {

+ 65 - 0
packages/dashboard/src/framework/internal/page/list-page.tsx

@@ -0,0 +1,65 @@
+import { DataTable } from '@/framework/internal/data-table/data-table.js';
+import {
+    getListQueryFields,
+    getQueryName,
+} from '@/framework/internal/document-introspection/get-document-structure.js';
+import { api } from '@/graphql/api.js';
+import { TypedDocumentNode } from '@graphql-typed-document-node/core';
+import { useQuery } from '@tanstack/react-query';
+import { createColumnHelper } from '@tanstack/react-table';
+import { ResultOf } from 'gql.tada';
+import { DocumentNode } from 'graphql';
+import React from 'react';
+
+type ListQueryFields<T extends TypedDocumentNode> = {
+    [Key in keyof ResultOf<T>]: ResultOf<T>[Key] extends { items: infer U }
+        ? U extends any[]
+            ? U[number]
+            : never
+        : never;
+}[keyof ResultOf<T>];
+
+export type CustomizeColumnConfig<T extends TypedDocumentNode> = {
+    [Key in keyof ListQueryFields<T>]?: {
+        header: string;
+    };
+};
+
+export interface ListPageProps<T extends TypedDocumentNode<U>, U extends { [key: string]: any }> {
+    title: string;
+    listQuery: T;
+    customizeColumns?: CustomizeColumnConfig<T>;
+}
+
+export function ListPage<T extends TypedDocumentNode<U>, U extends Record<string, any> = any>({
+    title,
+    listQuery,
+    customizeColumns,
+}: ListPageProps<T, U>) {
+    const { data } = useQuery({
+        queryFn: () =>
+            api.query(listQuery, {
+                options: {
+                    take: 10,
+                },
+            }),
+        queryKey: ['ProductList'],
+    });
+    const fields = getListQueryFields(listQuery);
+    const queryName = getQueryName(listQuery);
+    const columnHelper = createColumnHelper();
+
+    const columns = fields.map(field =>
+        columnHelper.accessor(field.name as any, {
+            header: customizeColumns?.[field.name as keyof ListQueryFields<T>]?.header ?? field.name,
+            meta: { type: field.type },
+        }),
+    );
+
+    return (
+        <div className="m-4">
+            <h1 className="text-2xl font-bold">{title}</h1>
+            <DataTable columns={columns} data={(data as any)?.[queryName]?.items ?? []}></DataTable>
+        </div>
+    );
+}

+ 13 - 29
packages/dashboard/src/routes/_authenticated/products.tsx

@@ -1,11 +1,6 @@
-import { DataTable } from '@/framework/internal/data-table/data-table.js';
-import { getListQueryFields } from '@/framework/internal/document-introspection/get-document-structure.js';
-import { api } from '@/graphql/api.js';
+import { ListPage } from '@/framework/internal/page/list-page.js';
 import { graphql } from '@/graphql/graphql.js';
-import { useQuery } from '@tanstack/react-query';
 import { createFileRoute } from '@tanstack/react-router';
-import { createColumnHelper } from '@tanstack/react-table';
-import { ResultOf } from 'gql.tada';
 import React from 'react';
 
 export const Route = createFileRoute('/_authenticated/products')({
@@ -17,35 +12,24 @@ const productListDocument = graphql(`
         products(options: $options) {
             items {
                 id
-                name
-                slug
-                enabled
+                createdAt
                 updatedAt
+                name
+                description
             }
         }
     }
 `);
 
 export function ProductListPage() {
-    const { data } = useQuery({
-        queryFn: () =>
-            api.query(productListDocument, {
-                options: {
-                    take: 100,
-                },
-            }),
-        queryKey: ['ProductList'],
-    });
-    const fields = getListQueryFields(productListDocument);
-    const columnHelper =
-        createColumnHelper<ResultOf<typeof productListDocument>['products']['items'][number]>();
-
-    const columns = fields.map(field =>
-        columnHelper.accessor(field.name as any, {
-            header: field.name,
-            meta: { type: field.type },
-        }),
+    return (
+        <ListPage
+            title="Products"
+            listQuery={productListDocument}
+            customizeColumns={{
+                id: { header: 'ID' },
+                name: { header: 'Name' },
+            }}
+        />
     );
-
-    return <DataTable columns={columns} data={data?.products.items ?? []}></DataTable>;
 }