@izumisy-tailor/tailor-data-viewer 0.1.30 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,12 +45,16 @@ For Tailor Platform apps using `@tailor-platform/app-shell`:
45
45
 
46
46
  ```typescript
47
47
  // src/shared/data-viewer.ts
48
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
48
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
49
49
  import { tableMetadata } from "./generated/table-metadata";
50
50
 
51
+ const fetcher = createDefaultFetcher({
52
+ endpoint: import.meta.env.VITE_APP_URL,
53
+ });
54
+
51
55
  export const DataViewer = createDataViewer({
52
56
  metadata: tableMetadata,
53
- appUri: import.meta.env.VITE_APP_URL,
57
+ fetcher,
54
58
  });
55
59
  ```
56
60
 
@@ -80,28 +84,47 @@ export const App = () => (
80
84
 
81
85
  For detailed options, see [AppShell Module Integration](./docs/app-shell-module.md).
82
86
 
83
- ### Standalone Component
87
+ ### Standalone Explorer View
84
88
 
85
- For custom React apps, set up the metadata generator as described in [Generating Table Metadata](#generating-table-metadata). Then use the generated metadata:
89
+ For custom React apps without AppShell, use `DataViewer.ExplorerView`. This provides a tab-based UI where users can select tables, create filters, and save views:
86
90
 
87
91
  ```tsx
88
- import { DataViewer, SavedViewProvider } from "@izumisy-tailor/tailor-data-viewer/component";
92
+ // src/lib/data-viewer.ts (create once per app)
93
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
89
94
  import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
90
- import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
91
-
92
- // Import the generated metadata from your Tailor Platform SDK project
93
95
  import { tableMetadata } from "./generated/table-metadata";
94
96
 
95
- const store = createIndexedDBStore();
97
+ const fetcher = createDefaultFetcher({
98
+ endpoint: "https://your-app.tailor.tech/graphql",
99
+ });
100
+
101
+ export const DataViewer = createDataViewer({
102
+ metadata: tableMetadata,
103
+ fetcher,
104
+ });
105
+
106
+ export const savedViewStore = createIndexedDBStore();
107
+ ```
108
+
109
+ ```tsx
110
+ // src/App.tsx
111
+ import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
112
+ import { DataViewer, savedViewStore } from "./lib/data-viewer";
96
113
 
97
114
  function App() {
98
115
  return (
99
- <SavedViewProvider store={store}>
100
- <DataViewer
101
- metadata={tableMetadata}
102
- appUri="https://your-app.tailor.tech/graphql"
103
- />
104
- </SavedViewProvider>
116
+ <DataViewer.ExplorerView savedViewStore={savedViewStore} />
117
+ );
118
+ }
119
+
120
+ // With initial view loaded from URL
121
+ function AppWithViewId() {
122
+ const viewId = useSearchParams().get("viewId") ?? undefined;
123
+ return (
124
+ <DataViewer.ExplorerView
125
+ savedViewStore={savedViewStore}
126
+ initialViewId={viewId}
127
+ />
105
128
  );
106
129
  }
107
130
  ```
@@ -110,16 +133,20 @@ For store options, see [Saved View Store](./docs/saved-view-store.md).
110
133
 
111
134
  ### Compositional API
112
135
 
113
- For more control over the UI layout and behavior, use the compositional (compound component) API:
136
+ For more control over the UI layout and behavior, use the compositional (compound component) API with `createDataViewer`:
114
137
 
115
138
  ```tsx
116
139
  // src/shared/data-viewer.ts
117
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
140
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
118
141
  import { tableMetadata } from "./generated/table-metadata";
119
142
 
143
+ const fetcher = createDefaultFetcher({
144
+ endpoint: "https://your-app.tailor.tech",
145
+ });
146
+
120
147
  export const DataViewer = createDataViewer({
121
148
  metadata: tableMetadata,
122
- appUri: "https://your-app.tailor.tech",
149
+ fetcher,
123
150
  });
124
151
  ```
125
152
 
package/docs/API.md CHANGED
@@ -4,21 +4,100 @@
4
4
 
5
5
  ### `createDataViewer`
6
6
 
7
- Creates a DataViewer instance with fixed metadata and appUri. Call this once per app and share the instance across pages.
7
+ Creates a DataViewer instance with fixed metadata and fetcher. Call this once per app and share the instance across pages.
8
8
 
9
9
  ```tsx
10
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
10
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
11
+
12
+ const fetcher = createDefaultFetcher({
13
+ endpoint: "https://your-app.tailor.tech",
14
+ });
11
15
 
12
16
  const DataViewer = createDataViewer({
13
17
  metadata: tableMetadata,
14
- appUri: "https://your-app.tailor.tech",
18
+ fetcher,
15
19
  });
16
20
 
17
21
  // Returns:
18
22
  // - DataViewer.TableDataProvider - Root provider for table data
19
23
  // - DataViewer.ToolbarProvider - Toolbar state provider
20
24
  // - DataViewer.metadata - The metadata passed to createDataViewer
21
- // - DataViewer.appUri - The appUri passed to createDataViewer
25
+ // - DataViewer.fetcher - The fetcher passed to createDataViewer
26
+ ```
27
+
28
+ ## GraphQL Fetcher
29
+
30
+ ### `GraphQLFetcher`
31
+
32
+ Interface for GraphQL data fetching. Implement this interface to use your own GraphQL client.
33
+
34
+ ```tsx
35
+ interface GraphQLFetcher {
36
+ execute: <T = unknown>(
37
+ query: string,
38
+ variables?: Record<string, unknown>,
39
+ options?: GraphQLFetcherOptions,
40
+ ) => Promise<GraphQLFetcherResult<T>>;
41
+ }
42
+
43
+ interface GraphQLFetcherOptions {
44
+ signal?: AbortSignal;
45
+ }
46
+
47
+ interface GraphQLFetcherResult<T> {
48
+ data: T | null;
49
+ errors?: GraphQLError[];
50
+ }
51
+ ```
52
+
53
+ ### `createDefaultFetcher`
54
+
55
+ Creates a default GraphQL fetcher using `graphql-request`.
56
+
57
+ ```tsx
58
+ import { createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
59
+
60
+ const fetcher = createDefaultFetcher({
61
+ endpoint: "https://your-app.tailor.tech/graphql",
62
+ headers: { "X-Custom-Header": "value" }, // optional
63
+ credentials: "include", // optional, default: "include"
64
+ });
65
+ ```
66
+
67
+ ### `createUrqlFetcher`
68
+
69
+ Creates a GraphQL fetcher from an existing urql Client.
70
+
71
+ ```tsx
72
+ import { createClient } from "@urql/core";
73
+ import { createUrqlFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
74
+
75
+ const urqlClient = createClient({
76
+ url: "https://your-app.tailor.tech/graphql",
77
+ fetchOptions: {
78
+ headers: { "Authorization": "Bearer token" },
79
+ },
80
+ });
81
+
82
+ const fetcher = createUrqlFetcher({ client: urqlClient });
83
+ ```
84
+
85
+ ### Custom Fetcher Implementation
86
+
87
+ You can implement your own fetcher for any GraphQL client:
88
+
89
+ ```tsx
90
+ const customFetcher: GraphQLFetcher = {
91
+ execute: async (query, variables, options) => {
92
+ const response = await fetch("/graphql", {
93
+ method: "POST",
94
+ headers: { "Content-Type": "application/json" },
95
+ body: JSON.stringify({ query, variables }),
96
+ signal: options?.signal,
97
+ });
98
+ return response.json();
99
+ },
100
+ };
22
101
  ```
23
102
 
24
103
  ## Components
@@ -31,8 +110,8 @@ Main component with tab-based interface (for AppShell integration).
31
110
  interface DataViewerProps {
32
111
  /** Map of table name to metadata */
33
112
  tableMetadata: TableMetadataMap;
34
- /** GraphQL endpoint URI */
35
- appUri: string;
113
+ /** GraphQL fetcher for data fetching */
114
+ fetcher: GraphQLFetcher;
36
115
  /** Optional initial view ID to load */
37
116
  initialViewId?: string;
38
117
  }
@@ -88,7 +167,7 @@ const {
88
167
  previousPage,
89
168
  resetPagination,
90
169
  refetch,
91
- } = useTableData(appUri, table, selectedFields, selectedRelations, filters, metadataMap, expandedFields);
170
+ } = useTableData(fetcher, table, selectedFields, selectedRelations, filters, metadataMap, expandedFields);
92
171
  ```
93
172
 
94
173
  ### `useColumnState`
@@ -14,12 +14,16 @@ First, create a shared DataViewer instance:
14
14
 
15
15
  ```typescript
16
16
  // src/shared/data-viewer.ts
17
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
17
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
18
18
  import { tableMetadata } from "@tailor-platform/my-app/generated/table-metadata";
19
19
 
20
+ const fetcher = createDefaultFetcher({
21
+ endpoint: import.meta.env.VITE_APP_URL,
22
+ });
23
+
20
24
  export const DataViewer = createDataViewer({
21
25
  metadata: tableMetadata,
22
- appUri: import.meta.env.VITE_APP_URL,
26
+ fetcher,
23
27
  });
24
28
  ```
25
29
 
@@ -18,12 +18,16 @@ First, create a shared DataViewer instance (once per app):
18
18
 
19
19
  ```tsx
20
20
  // src/shared/data-viewer.ts
21
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
21
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
22
22
  import { tableMetadata } from "./generated/table-metadata";
23
23
 
24
+ const fetcher = createDefaultFetcher({
25
+ endpoint: "https://your-app.tailor.tech",
26
+ });
27
+
24
28
  export const DataViewer = createDataViewer({
25
29
  metadata: tableMetadata,
26
- appUri: "https://your-app.tailor.tech",
30
+ fetcher,
27
31
  });
28
32
  ```
29
33
 
@@ -70,21 +74,25 @@ function MyDataViewer() {
70
74
 
71
75
  ### createDataViewer
72
76
 
73
- Factory function to create a DataViewer instance with fixed metadata and appUri. Call this once per app and share the instance across pages.
77
+ Factory function to create a DataViewer instance with fixed metadata and fetcher. Call this once per app and share the instance across pages.
74
78
 
75
79
  ```tsx
76
- import { createDataViewer } from "@izumisy-tailor/tailor-data-viewer/component";
80
+ import { createDataViewer, createDefaultFetcher } from "@izumisy-tailor/tailor-data-viewer/component";
81
+
82
+ const fetcher = createDefaultFetcher({
83
+ endpoint: "https://your-app.tailor.tech",
84
+ });
77
85
 
78
86
  const DataViewer = createDataViewer({
79
87
  metadata: tableMetadata, // Generated from TailorDB schema
80
- appUri: "https://your-app.tailor.tech",
88
+ fetcher,
81
89
  });
82
90
 
83
91
  // Returns:
84
92
  // - DataViewer.TableDataProvider - Root provider for table data
85
93
  // - DataViewer.ToolbarProvider - Toolbar state provider
86
94
  // - DataViewer.metadata - The metadata passed to createDataViewer
87
- // - DataViewer.appUri - The appUri passed to createDataViewer
95
+ // - DataViewer.fetcher - The fetcher passed to createDataViewer
88
96
  ```
89
97
 
90
98
  ### DataViewer.TableDataProvider
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@izumisy-tailor/tailor-data-viewer",
3
3
  "private": false,
4
- "version": "0.1.30",
4
+ "version": "0.1.32",
5
5
  "type": "module",
6
6
  "description": "Flexible data viewer component for Tailor Platform",
7
7
  "files": [
@@ -13,6 +13,7 @@ import {
13
13
  Columns,
14
14
  LayoutGrid,
15
15
  } from "lucide-react";
16
+ import type { TableMetadataMap } from "../generator/metadata-generator";
16
17
  import type { DataViewModuleConfig } from "./types";
17
18
  import { isStoreFactory } from "./types";
18
19
  import { DataViewer as DataViewerComponent } from "../component/data-viewer";
@@ -45,7 +46,9 @@ import { Badge } from "../component/ui/badge";
45
46
  * });
46
47
  * ```
47
48
  */
48
- export function createDataViewModule(config: DataViewModuleConfig) {
49
+ export function createDataViewModule<
50
+ TMetadata extends TableMetadataMap = TableMetadataMap,
51
+ >(config: DataViewModuleConfig<TMetadata>) {
49
52
  const {
50
53
  dataViewer,
51
54
  savedViewStore: storeOrFactory,
@@ -53,7 +56,7 @@ export function createDataViewModule(config: DataViewModuleConfig) {
53
56
  meta = {},
54
57
  } = config;
55
58
 
56
- const { metadata: tableMetadata, appUri } = dataViewer;
59
+ const { metadata: tableMetadata, fetcher } = dataViewer;
57
60
 
58
61
  const basePath = path.base ?? "data-view";
59
62
  const explorerPath = path.explorer ?? "explorer";
@@ -62,7 +65,7 @@ export function createDataViewModule(config: DataViewModuleConfig) {
62
65
 
63
66
  // Resolve store (call factory if needed)
64
67
  const store = isStoreFactory(storeOrFactory)
65
- ? storeOrFactory({ appUri })
68
+ ? storeOrFactory({ fetcher })
66
69
  : storeOrFactory;
67
70
 
68
71
  // Create the explorer resource page component
@@ -74,7 +77,7 @@ export function createDataViewModule(config: DataViewModuleConfig) {
74
77
  <SavedViewProvider store={store}>
75
78
  <DataViewerComponent
76
79
  metadata={tableMetadata}
77
- appUri={appUri}
80
+ fetcher={fetcher}
78
81
  initialViewId={viewId}
79
82
  />
80
83
  </SavedViewProvider>
@@ -11,6 +11,7 @@ import type {
11
11
  import { DataViewerProvider } from "./contexts";
12
12
  import { ToolbarProvider } from "./contexts";
13
13
  import type { ReactNode } from "react";
14
+ import { createMockFetcher } from "../tests/helper";
14
15
 
15
16
  const mockFields: FieldMetadata[] = [
16
17
  { name: "id", type: "uuid", required: true, description: "ID" },
@@ -48,7 +49,7 @@ function TestWrapper({
48
49
  }: WrapperProps) {
49
50
  return (
50
51
  <DataViewerProvider
51
- appUri="https://example.com"
52
+ fetcher={createMockFetcher()}
52
53
  tableName={tableMetadata.name}
53
54
  metadata={metadata}
54
55
  initialData={{
@@ -4,6 +4,7 @@ import {
4
4
  type DataViewerProviderProps,
5
5
  } from "./data-viewer-context";
6
6
  import type { TableMetadataMap } from "../../generator/metadata-generator";
7
+ import { createMockFetcher } from "../../tests/helper";
7
8
 
8
9
  // =============================================================================
9
10
  // Mock metadata with `as const` for type-safe tests
@@ -135,14 +136,14 @@ describe("DataViewerProvider type safety", () => {
135
136
  children: null,
136
137
  tableName: "user",
137
138
  metadata: mockMetadata,
138
- appUri: "https://example.com",
139
+ fetcher: createMockFetcher(),
139
140
  };
140
141
 
141
142
  const postProps: DataViewerProviderProps<MockMetadata, "post"> = {
142
143
  children: null,
143
144
  tableName: "post",
144
145
  metadata: mockMetadata,
145
- appUri: "https://example.com",
146
+ fetcher: createMockFetcher(),
146
147
  };
147
148
 
148
149
  expect(userProps.tableName).toBe("user");
@@ -11,6 +11,7 @@ import type {
11
11
  TableMetadataMap,
12
12
  ExpandedRelationFields,
13
13
  } from "../../generator/metadata-generator";
14
+ import type { GraphQLFetcher } from "../../graphql/fetcher";
14
15
  import { useColumnState } from "../hooks/use-column-state";
15
16
  import type { SearchFilters } from "../types";
16
17
  import type { SortState } from "../hooks/use-table-data";
@@ -71,7 +72,7 @@ export interface DataViewerContextValue {
71
72
  // Metadata
72
73
  tableMetadata: TableMetadata | null;
73
74
  metadata: TableMetadataMap;
74
- appUri: string;
75
+ fetcher: GraphQLFetcher;
75
76
 
76
77
  // Column state
77
78
  selectedFields: string[];
@@ -148,8 +149,8 @@ export interface DataViewerProviderProps<
148
149
  tableName: TTableName;
149
150
  /** All table metadata (generated from TailorDB schema) */
150
151
  metadata: TMetadata;
151
- /** App URI for GraphQL endpoint */
152
- appUri: string;
152
+ /** GraphQL fetcher for data fetching */
153
+ fetcher: GraphQLFetcher;
153
154
  /** Initial data for filters, selected fields, and relations (type-safe when metadata is `as const`) */
154
155
  initialData?: DataViewerInitialData<TMetadata, TTableName>;
155
156
  }
@@ -165,7 +166,7 @@ export function DataViewerProvider<
165
166
  children,
166
167
  tableName,
167
168
  metadata,
168
- appUri,
169
+ fetcher,
169
170
  initialData,
170
171
  }: DataViewerProviderProps<TMetadata, TTableName>) {
171
172
  // Get table metadata from the map
@@ -213,7 +214,7 @@ export function DataViewerProvider<
213
214
  () => ({
214
215
  tableMetadata,
215
216
  metadata,
216
- appUri,
217
+ fetcher,
217
218
  selectedFields: columnState.selectedFields,
218
219
  toggleField: columnState.toggleField,
219
220
  selectAllFields: columnState.selectAll,
@@ -234,7 +235,7 @@ export function DataViewerProvider<
234
235
  [
235
236
  tableMetadata,
236
237
  metadata,
237
- appUri,
238
+ fetcher,
238
239
  columnState.selectedFields,
239
240
  columnState.toggleField,
240
241
  columnState.selectAll,
@@ -9,6 +9,7 @@ import type {
9
9
  TableMetadataMap,
10
10
  ExpandedRelationFields,
11
11
  } from "../../generator/metadata-generator";
12
+ import type { GraphQLFetcher } from "../../graphql/fetcher";
12
13
  import {
13
14
  useTableData,
14
15
  type SortState,
@@ -54,8 +55,8 @@ export interface TableDataProviderProps<
54
55
  tableName: TTableName;
55
56
  /** All table metadata (generated from TailorDB schema) */
56
57
  metadata: TMetadata;
57
- /** App URI for GraphQL endpoint */
58
- appUri: string;
58
+ /** GraphQL fetcher for data fetching */
59
+ fetcher: GraphQLFetcher;
59
60
  /** Initial data for filters, selected fields, and relations (type-safe when metadata is `as const`) */
60
61
  initialData?: DataViewerInitialData<TMetadata, TTableName>;
61
62
  }
@@ -71,14 +72,14 @@ export function TableDataProvider<
71
72
  children,
72
73
  tableName,
73
74
  metadata,
74
- appUri,
75
+ fetcher,
75
76
  initialData,
76
77
  }: TableDataProviderProps<TMetadata, TTableName>) {
77
78
  return (
78
79
  <DataViewerProvider
79
80
  tableName={tableName}
80
81
  metadata={metadata}
81
- appUri={appUri}
82
+ fetcher={fetcher}
82
83
  initialData={initialData}
83
84
  >
84
85
  <TableDataProviderInner>{children}</TableDataProviderInner>
@@ -92,7 +93,7 @@ export function TableDataProvider<
92
93
  */
93
94
  function TableDataProviderInner({ children }: { children: ReactNode }) {
94
95
  const {
95
- appUri,
96
+ fetcher,
96
97
  tableMetadata,
97
98
  metadata,
98
99
  selectedFields,
@@ -105,7 +106,7 @@ function TableDataProviderInner({ children }: { children: ReactNode }) {
105
106
 
106
107
  // Fetch table data
107
108
  const tableData = useTableData(
108
- appUri,
109
+ fetcher,
109
110
  tableMetadata,
110
111
  selectedFields,
111
112
  selectedRelations,
@@ -1,8 +1,12 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { TableMetadataMap } from "../generator/metadata-generator";
3
+ import type { GraphQLFetcher } from "../graphql/fetcher";
4
+ import type { SavedViewStore, SavedViewStoreFactory } from "../store/types";
3
5
  import { type DataViewerInitialData } from "./contexts/data-viewer-context";
4
6
  import { TableDataProvider } from "./contexts/table-data-context";
5
7
  import { ToolbarProvider } from "./contexts/toolbar-context";
8
+ import { SavedViewProvider } from "./saved-view-context";
9
+ import { DataViewer as DataViewerExplorer } from "./data-viewer";
6
10
 
7
11
  /**
8
12
  * Configuration for createDataViewer factory
@@ -10,8 +14,8 @@ import { ToolbarProvider } from "./contexts/toolbar-context";
10
14
  export interface CreateDataViewerConfig<TMetadata extends TableMetadataMap> {
11
15
  /** All table metadata (generated from TailorDB schema) */
12
16
  metadata: TMetadata;
13
- /** App URI for GraphQL endpoint */
14
- appUri: string;
17
+ /** GraphQL fetcher for data fetching */
18
+ fetcher: GraphQLFetcher;
15
19
  }
16
20
 
17
21
  /**
@@ -35,6 +39,20 @@ export interface DataViewerToolbarProviderProps {
35
39
  children: ReactNode;
36
40
  }
37
41
 
42
+ /**
43
+ * Props for DataViewer.ExplorerView (tab-based UI)
44
+ */
45
+ export interface ExplorerViewProps {
46
+ /**
47
+ * Saved view store for persisting views.
48
+ * Can be a store instance or a factory function that receives the fetcher.
49
+ * If not provided, views won't be persisted.
50
+ */
51
+ savedViewStore?: SavedViewStore | SavedViewStoreFactory;
52
+ /** Initial view ID to load on mount */
53
+ initialViewId?: string;
54
+ }
55
+
38
56
  /**
39
57
  * Return type of createDataViewer factory
40
58
  */
@@ -53,29 +71,39 @@ export interface DataViewer<TMetadata extends TableMetadataMap> {
53
71
  */
54
72
  ToolbarProvider: (props: DataViewerToolbarProviderProps) => ReactNode;
55
73
 
74
+ /**
75
+ * Tab-based explorer view component - provides a full-featured data exploration UI
76
+ * with multiple tabs, table selection, and saved views support
77
+ */
78
+ ExplorerView: (props: ExplorerViewProps) => ReactNode;
79
+
56
80
  /**
57
81
  * The metadata passed to createDataViewer
58
82
  */
59
83
  metadata: TMetadata;
60
84
 
61
85
  /**
62
- * The appUri passed to createDataViewer
86
+ * The fetcher passed to createDataViewer
63
87
  */
64
- appUri: string;
88
+ fetcher: GraphQLFetcher;
65
89
  }
66
90
 
67
91
  /**
68
- * Factory function to create a DataViewer instance with fixed metadata and appUri
92
+ * Factory function to create a DataViewer instance with fixed metadata and fetcher
69
93
  *
70
94
  * @example
71
95
  * ```tsx
72
96
  * // src/lib/data-viewer.ts (create once per app)
73
- * import { createDataViewer } from "@tailor-platform/data-viewer";
97
+ * import { createDataViewer, createDefaultFetcher } from "@tailor-platform/data-viewer";
74
98
  * import { tableMetadata } from "./generated/metadata";
75
99
  *
100
+ * const fetcher = createDefaultFetcher({
101
+ * endpoint: import.meta.env.VITE_APP_URL,
102
+ * });
103
+ *
76
104
  * export const DataViewer = createDataViewer({
77
105
  * metadata: tableMetadata,
78
- * appUri: import.meta.env.VITE_APP_URL,
106
+ * fetcher,
79
107
  * });
80
108
  *
81
109
  * // pages/suppliers/page.tsx (use in each page)
@@ -99,9 +127,9 @@ export interface DataViewer<TMetadata extends TableMetadataMap> {
99
127
  export function createDataViewer<TMetadata extends TableMetadataMap>(
100
128
  config: CreateDataViewerConfig<TMetadata>,
101
129
  ): DataViewer<TMetadata> {
102
- const { metadata, appUri } = config;
130
+ const { metadata, fetcher } = config;
103
131
 
104
- // TableDataProvider with fixed metadata and appUri
132
+ // TableDataProvider with fixed metadata and fetcher
105
133
  function TableDataProviderWrapper<
106
134
  TTableName extends keyof TMetadata & string,
107
135
  >({
@@ -112,7 +140,7 @@ export function createDataViewer<TMetadata extends TableMetadataMap>(
112
140
  return (
113
141
  <TableDataProvider
114
142
  metadata={metadata}
115
- appUri={appUri}
143
+ fetcher={fetcher}
116
144
  tableName={tableName}
117
145
  initialData={initialData}
118
146
  >
@@ -128,10 +156,41 @@ export function createDataViewer<TMetadata extends TableMetadataMap>(
128
156
  return <ToolbarProvider>{children}</ToolbarProvider>;
129
157
  }
130
158
 
159
+ // ExplorerView - tab-based UI with saved views support
160
+ function ExplorerViewWrapper({
161
+ savedViewStore,
162
+ initialViewId,
163
+ }: ExplorerViewProps): ReactNode {
164
+ // Resolve store (call factory if needed)
165
+ const store = savedViewStore
166
+ ? typeof savedViewStore === "function"
167
+ ? savedViewStore({ fetcher })
168
+ : savedViewStore
169
+ : undefined;
170
+
171
+ const explorerContent = (
172
+ <DataViewerExplorer
173
+ metadata={metadata}
174
+ fetcher={fetcher}
175
+ initialViewId={initialViewId}
176
+ />
177
+ );
178
+
179
+ // Wrap with SavedViewProvider only if store is provided
180
+ if (store) {
181
+ return (
182
+ <SavedViewProvider store={store}>{explorerContent}</SavedViewProvider>
183
+ );
184
+ }
185
+
186
+ return explorerContent;
187
+ }
188
+
131
189
  return {
132
190
  TableDataProvider: TableDataProviderWrapper,
133
191
  ToolbarProvider: ToolbarProviderWrapper,
192
+ ExplorerView: ExplorerViewWrapper,
134
193
  metadata,
135
- appUri,
194
+ fetcher,
136
195
  };
137
196
  }