@izumisy-tailor/tailor-data-viewer 0.1.3 → 0.1.5

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.
Files changed (48) hide show
  1. package/README.md +44 -4
  2. package/dist/generator/index.d.mts +7 -1
  3. package/dist/generator/index.mjs +1 -1
  4. package/docs/app-shell-module.md +151 -0
  5. package/docs/saved-view-store.md +155 -0
  6. package/package.json +27 -2
  7. package/src/app-shell/create-data-view-module.tsx +84 -0
  8. package/src/app-shell/index.ts +2 -0
  9. package/src/app-shell/types.ts +42 -0
  10. package/src/component/column-selector.tsx +4 -4
  11. package/src/component/data-table.tsx +2 -2
  12. package/src/component/data-view-tab-content.tsx +3 -3
  13. package/src/component/data-viewer.tsx +38 -9
  14. package/src/component/hooks/use-accessible-tables.ts +1 -1
  15. package/src/component/hooks/use-column-state.ts +1 -1
  16. package/src/component/hooks/use-relation-data.ts +1 -1
  17. package/src/component/hooks/use-table-access-check.ts +103 -0
  18. package/src/component/hooks/use-table-data.ts +1 -1
  19. package/src/component/index.ts +4 -1
  20. package/src/component/pagination.tsx +1 -1
  21. package/src/component/relation-content.tsx +4 -4
  22. package/src/component/saved-view-context.tsx +195 -48
  23. package/src/component/search-filter.tsx +8 -8
  24. package/src/component/single-record-tab-content.tsx +5 -5
  25. package/src/component/table-selector.tsx +2 -2
  26. package/src/component/types.ts +1 -1
  27. package/src/component/view-save-load.tsx +4 -4
  28. package/src/generator/metadata-generator.ts +8 -1
  29. package/src/store/indexeddb.ts +150 -0
  30. package/src/store/tailordb/index.ts +204 -0
  31. package/src/store/tailordb/schema.ts +114 -0
  32. package/src/store/types.ts +85 -0
  33. package/src/utils/query-builder.ts +1 -1
  34. package/src/types/table-metadata.ts +0 -72
  35. /package/{API.md → docs/API.md} +0 -0
  36. /package/src/{lib → component/lib}/utils.ts +0 -0
  37. /package/src/{ui → component/ui}/alert.tsx +0 -0
  38. /package/src/{ui → component/ui}/badge.tsx +0 -0
  39. /package/src/{ui → component/ui}/button.tsx +0 -0
  40. /package/src/{ui → component/ui}/card.tsx +0 -0
  41. /package/src/{ui → component/ui}/checkbox.tsx +0 -0
  42. /package/src/{ui → component/ui}/collapsible.tsx +0 -0
  43. /package/src/{ui → component/ui}/dialog.tsx +0 -0
  44. /package/src/{ui → component/ui}/dropdown-menu.tsx +0 -0
  45. /package/src/{ui → component/ui}/input.tsx +0 -0
  46. /package/src/{ui → component/ui}/label.tsx +0 -0
  47. /package/src/{ui → component/ui}/select.tsx +0 -0
  48. /package/src/{ui → component/ui}/table.tsx +0 -0
package/README.md CHANGED
@@ -34,23 +34,61 @@ npm install react react-dom
34
34
 
35
35
  ## Usage
36
36
 
37
- ### Basic Setup
37
+ There are two ways to use Data Viewer:
38
38
 
39
- First, set up the metadata generator as described in [Generating Table Metadata](#generating-table-metadata). Then use the generated metadata in your React app:
39
+ 1. **AppShell Module** - Recommended for Tailor Platform apps
40
+ 2. **Standalone Component** - For custom React apps
41
+
42
+ ### AppShell Module (Recommended)
43
+
44
+ For Tailor Platform apps using `@tailor-platform/app-shell`:
45
+
46
+ ```typescript
47
+ // src/pages/data-view/index.ts
48
+ import { createDataViewModule } from "@izumisy-tailor/tailor-data-viewer/app-shell";
49
+ import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
50
+ import { tableMetadata } from "./generated/table-metadata";
51
+
52
+ export const dataViewModule = createDataViewModule({
53
+ tableMetadata,
54
+ appUri: import.meta.env.VITE_APP_URL,
55
+ store: createIndexedDBStore(),
56
+ });
57
+ ```
58
+
59
+ ```typescript
60
+ // src/app.tsx
61
+ import { AppShell } from "@tailor-platform/app-shell";
62
+ import { dataViewModule } from "./pages/data-view";
63
+
64
+ export const App = () => (
65
+ <AppShell
66
+ modules={[dataViewModule]}
67
+ />
68
+ );
69
+ ```
70
+
71
+ For detailed options, see [AppShell Module Integration](./docs/app-shell-module.md).
72
+
73
+ ### Standalone Component
74
+
75
+ For custom React apps, set up the metadata generator as described in [Generating Table Metadata](#generating-table-metadata). Then use the generated metadata:
40
76
 
41
77
  ```tsx
42
78
  import { DataViewer, SavedViewProvider } from "@izumisy-tailor/tailor-data-viewer/component";
79
+ import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
43
80
  import "@izumisy-tailor/tailor-data-viewer/styles/theme.css";
44
81
 
45
82
  // Import the generated metadata from your Tailor Platform SDK project
46
83
  import { tableMetadata } from "./generated/table-metadata";
47
84
 
85
+ const store = createIndexedDBStore();
86
+
48
87
  function App() {
49
88
  return (
50
- <SavedViewProvider>
89
+ <SavedViewProvider store={store}>
51
90
  <DataViewer
52
91
  tableMetadata={tableMetadata}
53
- userRoles={["admin"]}
54
92
  appUri="https://your-app.tailor.tech/graphql"
55
93
  />
56
94
  </SavedViewProvider>
@@ -58,6 +96,8 @@ function App() {
58
96
  }
59
97
  ```
60
98
 
99
+ For store options, see [Saved View Store](./docs/saved-view-store.md).
100
+
61
101
  ### Using Individual Components
62
102
 
63
103
  ```tsx
@@ -52,6 +52,12 @@ interface TableMetadata {
52
52
  * Map of all tables
53
53
  */
54
54
  type TableMetadataMap = Record<string, TableMetadata>;
55
+ /**
56
+ * Expanded relation fields configuration
57
+ * Key: relation field name (e.g., "task")
58
+ * Value: array of selected field names from the related table (e.g., ["name", "status"])
59
+ */
60
+ type ExpandedRelationFields = Record<string, string[]>;
55
61
  /**
56
62
  * Intermediate type for processed table data
57
63
  */
@@ -167,4 +173,4 @@ declare const tableMetadataGenerator: {
167
173
  }): GeneratorResult;
168
174
  };
169
175
  //#endregion
170
- export { FieldMetadata, FieldType, RelationMetadata, TableMetadata, TableMetadataMap, tableMetadataGenerator as default, tableMetadataGenerator };
176
+ export { ExpandedRelationFields, FieldMetadata, FieldType, RelationMetadata, TableMetadata, TableMetadataMap, tableMetadataGenerator as default, tableMetadataGenerator };
@@ -142,7 +142,7 @@ import type {
142
142
  RelationMetadata,
143
143
  TableMetadata,
144
144
  TableMetadataMap,
145
- } from "../../generator/table-metadata-generator";
145
+ } from "@izumisy-tailor/tailor-data-viewer/generator";
146
146
 
147
147
  export type { FieldType, FieldMetadata, RelationMetadata, TableMetadata, TableMetadataMap };
148
148
 
@@ -0,0 +1,151 @@
1
+ # AppShell Module Integration
2
+
3
+ Using the `createDataViewModule` factory function, you can easily integrate Data Viewer as a module into the Tailor Platform AppShell.
4
+
5
+ ## Overview
6
+
7
+ - **Zero Configuration**: Integrate Data Viewer into AppShell with minimal setup
8
+ - **Flexible Storage**: Save views to IndexedDB (local) or TailorDB (server)
9
+ - **Runtime Permission Checks**: Uses server-side `gqlPermission` as Single Source of Truth
10
+
11
+ ## Basic Usage
12
+
13
+ ### IndexedDB Store (Local Storage)
14
+
15
+ ```typescript
16
+ // src/pages/data-view/index.ts
17
+ import { createDataViewModule } from "@izumisy-tailor/tailor-data-viewer/app-shell";
18
+ import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
19
+ import { tableMetadata } from "@tailor-platform/my-app/generated/table-metadata";
20
+
21
+ export const dataViewModule = createDataViewModule({
22
+ tableMetadata,
23
+ appUri: import.meta.env.VITE_APP_URL,
24
+ store: createIndexedDBStore(),
25
+ });
26
+ ```
27
+
28
+ ### TailorDB Store (Server Storage)
29
+
30
+ ```typescript
31
+ // src/pages/data-view/index.ts
32
+ import { createDataViewModule } from "@izumisy-tailor/tailor-data-viewer/app-shell";
33
+ import { createTailorDBStore } from "@izumisy-tailor/tailor-data-viewer/store/tailordb";
34
+ import { tableMetadata } from "@tailor-platform/my-app/generated/table-metadata";
35
+
36
+ export const dataViewModule = createDataViewModule({
37
+ tableMetadata,
38
+ appUri: import.meta.env.VITE_APP_URL,
39
+ store: createTailorDBStore(),
40
+ });
41
+ ```
42
+
43
+ ### Usage with AppShell
44
+
45
+ ```typescript
46
+ // src/app.tsx
47
+ import { AppShell } from "@tailor-platform/app-shell";
48
+ import { dataViewModule } from "./pages/data-view";
49
+
50
+ export const App = () => (
51
+ <AppShell
52
+ modules={[
53
+ dataViewModule,
54
+ // ...other modules
55
+ ]}
56
+ />
57
+ );
58
+ ```
59
+
60
+ ## Configuration Options
61
+
62
+ ```typescript
63
+ interface DataViewModuleConfig {
64
+ /** Table metadata (required) */
65
+ tableMetadata: TableMetadataMap;
66
+
67
+ /** GraphQL API URI (required) */
68
+ appUri: string;
69
+
70
+ /** Store implementation for view persistence (required) */
71
+ store: SavedViewStore | SavedViewStoreFactory;
72
+
73
+ /** Routing configuration (optional) */
74
+ path?: {
75
+ /** Module base path (default: "data-view") */
76
+ base?: string;
77
+ /** Explorer resource path (default: "explorer") */
78
+ explorer?: string;
79
+ };
80
+
81
+ /** Module meta information (optional) */
82
+ meta?: {
83
+ /** Title (default: "Data Viewer") */
84
+ title?: string;
85
+ /** Icon (default: <Database />) */
86
+ icon?: ReactNode;
87
+ };
88
+ }
89
+ ```
90
+
91
+ ## Store Selection
92
+
93
+ | Store | Features | Use Case |
94
+ |-------|----------|----------|
95
+ | **IndexedDB** | Local storage, instant availability | Development, personal use, offline support |
96
+ | **TailorDB** | Server storage, team sharing | Production, team collaboration |
97
+
98
+ ### IndexedDB Store Options
99
+
100
+ ```typescript
101
+ const store = createIndexedDBStore({
102
+ dbName: "my-app-data-viewer", // default: "data-viewer"
103
+ storeName: "saved-views", // default: "savedViews"
104
+ });
105
+ ```
106
+
107
+ ### TailorDB Store Setup
108
+
109
+ When using TailorDB Store, you need the `DataViewerSavedView` table in your Tailor Platform.
110
+
111
+ ```typescript
112
+ // tailor.config.ts
113
+ import { defineConfig } from "@tailor-platform/sdk";
114
+
115
+ export default defineConfig({
116
+ name: "my-app",
117
+ db: {
118
+ "my-db": {
119
+ files: [
120
+ "db/**/*.ts",
121
+ "node_modules/@izumisy-tailor/tailor-data-viewer/src/store/tailordb/schema.ts",
122
+ ],
123
+ },
124
+ },
125
+ });
126
+ ```
127
+
128
+ ## Custom Store Implementation
129
+
130
+ To use your own persistence backend, implement the `SavedViewStore` interface:
131
+
132
+ ```typescript
133
+ import type { SavedViewStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
134
+
135
+ const customStore: SavedViewStore = {
136
+ listViews: async () => { /* implementation */ },
137
+ saveView: async (view) => { /* implementation */ },
138
+ deleteView: async (viewId) => { /* implementation */ },
139
+ getView: async (viewId) => { /* implementation */ },
140
+ };
141
+
142
+ // Or implement as a Factory (if you need appUri)
143
+ const customStoreFactory = (config: { appUri: string }) => ({
144
+ listViews: async () => { /* can use config.appUri */ },
145
+ // ...
146
+ });
147
+ ```
148
+
149
+ ## Related Documentation
150
+
151
+ - [Saved View Store](./saved-view-store.md) - Detailed store design and implementation
@@ -0,0 +1,155 @@
1
+ # Saved View Store
2
+
3
+ A storage system for persisting view configurations (selected columns, filters, sort order, etc.).
4
+
5
+ ## Overview
6
+
7
+ Data Viewer allows users to save and load view configurations. Two store implementations are provided:
8
+
9
+ - **IndexedDB Store**: Saves to browser local storage
10
+ - **TailorDB Store**: Saves to Tailor Platform database
11
+
12
+ ## SavedViewStore Interface
13
+
14
+ ```typescript
15
+ interface SavedViewStore {
16
+ /** Get list of saved views */
17
+ listViews(): Promise<SavedView[]>;
18
+
19
+ /** Save a view (create or update) */
20
+ saveView(view: SavedViewInput): Promise<SavedView>;
21
+
22
+ /** Delete a view */
23
+ deleteView(viewId: string): Promise<void>;
24
+
25
+ /** Get a specific view */
26
+ getView(viewId: string): Promise<SavedView | null>;
27
+ }
28
+ ```
29
+
30
+ ## SavedView Structure
31
+
32
+ ```typescript
33
+ interface SavedView {
34
+ id: string;
35
+ name: string;
36
+ tableName: string;
37
+ columns: string[];
38
+ filters: SearchFilters;
39
+ sortOrder: SortOrder[];
40
+ selectedRelations: string[];
41
+ expandedRelationFields: ExpandedRelationFields;
42
+ createdAt: Date;
43
+ updatedAt: Date;
44
+ }
45
+
46
+ interface SortOrder {
47
+ field: string;
48
+ direction: "asc" | "desc";
49
+ }
50
+ ```
51
+
52
+ ## IndexedDB Store
53
+
54
+ Saves locally using browser IndexedDB. A lightweight implementation using the [idb](https://github.com/jakearchibald/idb) library.
55
+
56
+ ### Features
57
+
58
+ - Not shared across devices
59
+ - Works offline
60
+ - Ready to use immediately (no server setup required)
61
+
62
+ ### Usage
63
+
64
+ ```typescript
65
+ import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
66
+
67
+ // Default configuration
68
+ const store = createIndexedDBStore();
69
+
70
+ // Custom configuration
71
+ const store = createIndexedDBStore({
72
+ dbName: "my-app-data-viewer", // default: "data-viewer"
73
+ storeName: "saved-views", // default: "savedViews"
74
+ });
75
+ ```
76
+
77
+ ## TailorDB Store
78
+
79
+ Saves to server using Tailor Platform database.
80
+
81
+ ### Features
82
+
83
+ - Shareable across team
84
+ - Per-user permission management
85
+ - Synced across devices
86
+
87
+ ### Setup
88
+
89
+ 1. **Add schema to tailor.config.ts**
90
+
91
+ ```typescript
92
+ // tailor.config.ts
93
+ import { defineConfig } from "@tailor-platform/sdk";
94
+
95
+ export default defineConfig({
96
+ name: "my-app",
97
+ db: {
98
+ "my-db": {
99
+ files: [
100
+ "db/**/*.ts",
101
+ "node_modules/@izumisy-tailor/tailor-data-viewer/src/store/tailordb/schema.ts",
102
+ ],
103
+ },
104
+ },
105
+ });
106
+ ```
107
+
108
+ 2. **Use the store**
109
+
110
+ ```typescript
111
+ import { createTailorDBStore } from "@izumisy-tailor/tailor-data-viewer/store/tailordb";
112
+
113
+ // appUri is automatically passed from createDataViewModule
114
+ const store = createTailorDBStore();
115
+ ```
116
+
117
+ ### Schema Details
118
+
119
+ The `DataViewerSavedView` table has the following fields:
120
+
121
+ | Field | Type | Description |
122
+ |-------|------|-------------|
123
+ | `name` | string | View name |
124
+ | `tableName` | string (indexed) | Target table name |
125
+ | `columns` | string[] | Selected columns |
126
+ | `filters` | string (JSON) | Filter conditions |
127
+ | `sortOrder` | string (JSON) | Sort order |
128
+ | `selectedRelations` | string[] | Selected relations |
129
+ | `expandedRelationFields` | string (JSON) | Expanded relation fields |
130
+ | `isDefault` | boolean | Whether this is the default view |
131
+ | `createdBy` | uuid | Creator (set automatically via hooks) |
132
+ | `createdAt` / `updatedAt` | datetime | Timestamps |
133
+
134
+ ### Permissions
135
+
136
+ - **create**: Logged-in users only
137
+ - **read/update/delete**: Creator only (restricted by `createdBy`)
138
+
139
+ ## SavedViewStoreFactory
140
+
141
+ When a store needs configuration like `appUri`, use the Factory pattern:
142
+
143
+ ```typescript
144
+ type SavedViewStoreFactory = (config: { appUri: string }) => SavedViewStore;
145
+ ```
146
+
147
+ When you pass a Factory to `createDataViewModule`, it internally calls the factory with `appUri` as an argument:
148
+
149
+ ```typescript
150
+ // TailorDB Store returns a Factory
151
+ const storeFactory = createTailorDBStore();
152
+
153
+ // Internally in createDataViewModule, it's called like:
154
+ const store = storeFactory({ appUri: "https://..." });
155
+ ```
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@izumisy-tailor/tailor-data-viewer",
3
3
  "private": false,
4
- "version": "0.1.3",
4
+ "version": "0.1.5",
5
5
  "type": "module",
6
6
  "description": "Flexible data viewer component for Tailor Platform",
7
7
  "files": [
8
8
  "src",
9
9
  "dist",
10
- "API.md"
10
+ "docs"
11
11
  ],
12
12
  "exports": {
13
13
  "./component": {
@@ -19,6 +19,18 @@
19
19
  },
20
20
  "./styles": {
21
21
  "default": "./src/styles/theme.css"
22
+ },
23
+ "./app-shell": {
24
+ "default": "./src/app-shell/index.ts"
25
+ },
26
+ "./store/indexeddb": {
27
+ "default": "./src/store/indexeddb.ts"
28
+ },
29
+ "./store/tailordb": {
30
+ "default": "./src/store/tailordb/index.ts"
31
+ },
32
+ "./store/tailordb/schema": {
33
+ "default": "./src/store/tailordb/schema.ts"
22
34
  }
23
35
  },
24
36
  "dependencies": {
@@ -32,14 +44,27 @@
32
44
  "class-variance-authority": "^0.7.1",
33
45
  "clsx": "^2.1.1",
34
46
  "graphql-request": "^6.1.0",
47
+ "idb": "^8.0.3",
35
48
  "lucide-react": "^0.468.0",
36
49
  "tailwind-merge": "^2.6.0"
37
50
  },
38
51
  "peerDependencies": {
52
+ "@tailor-platform/app-shell": ">=0.20.0",
53
+ "@tailor-platform/sdk": "^1.6.0",
39
54
  "react": "^18.0.0 || ^19.0.0",
40
55
  "react-dom": "^18.0.0 || ^19.0.0"
41
56
  },
57
+ "peerDependenciesMeta": {
58
+ "@tailor-platform/app-shell": {
59
+ "optional": true
60
+ },
61
+ "@tailor-platform/sdk": {
62
+ "optional": true
63
+ }
64
+ },
42
65
  "devDependencies": {
66
+ "@tailor-platform/app-shell": "^0.24.0",
67
+ "@tailor-platform/sdk": "^1.6.3",
43
68
  "@types/react": "^19.0.0",
44
69
  "@types/react-dom": "^19.0.0",
45
70
  "react": "^19.0.0",
@@ -0,0 +1,84 @@
1
+ import { defineModule, defineResource } from "@tailor-platform/app-shell";
2
+ import { Database } from "lucide-react";
3
+ import type { DataViewModuleConfig } from "./types";
4
+ import { isStoreFactory } from "./types";
5
+ import { DataViewer } from "../component/data-viewer";
6
+ import { SavedViewProvider } from "../component/saved-view-context";
7
+
8
+ /**
9
+ * Create a DataView module for use with AppShell
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { createDataViewModule } from "@izumisy-tailor/tailor-data-viewer/app-shell";
14
+ * import { createIndexedDBStore } from "@izumisy-tailor/tailor-data-viewer/store/indexeddb";
15
+ * import { tableMetadata } from "@tailor-platform/my-app/generated/table-metadata";
16
+ *
17
+ * export const dataViewModule = createDataViewModule({
18
+ * tableMetadata,
19
+ * appUri: import.meta.env.VITE_APP_URL,
20
+ * store: createIndexedDBStore(),
21
+ * });
22
+ * ```
23
+ */
24
+ export function createDataViewModule(config: DataViewModuleConfig) {
25
+ const {
26
+ tableMetadata,
27
+ appUri,
28
+ store: storeOrFactory,
29
+ path = {},
30
+ meta = {},
31
+ } = config;
32
+
33
+ const basePath = path.base ?? "data-view";
34
+ const explorerPath = path.explorer ?? "explorer";
35
+ const title = meta.title ?? "Data Viewer";
36
+ const icon = meta.icon ?? <Database />;
37
+
38
+ // Resolve store (call factory if needed)
39
+ const store = isStoreFactory(storeOrFactory)
40
+ ? storeOrFactory({ appUri })
41
+ : storeOrFactory;
42
+
43
+ // Create the explorer resource page component
44
+ const ExplorerPage = () => {
45
+ return (
46
+ <SavedViewProvider store={store}>
47
+ <DataViewer tableMetadata={tableMetadata} appUri={appUri} />
48
+ </SavedViewProvider>
49
+ );
50
+ };
51
+
52
+ // Create the module page component
53
+ const ModulePage = () => {
54
+ return (
55
+ <div className="space-y-4">
56
+ <h1 className="text-2xl font-bold">{title}</h1>
57
+ <p className="text-muted-foreground">
58
+ データビューでテーブルを探索できます。
59
+ </p>
60
+ </div>
61
+ );
62
+ };
63
+
64
+ // Define the explorer resource
65
+ const explorerResource = defineResource({
66
+ path: explorerPath,
67
+ component: ExplorerPage,
68
+ meta: {
69
+ title: "Explorer",
70
+ breadcrumbTitle: () => "Explorer",
71
+ },
72
+ });
73
+
74
+ // Define and return the module
75
+ return defineModule({
76
+ path: basePath,
77
+ component: ModulePage,
78
+ meta: {
79
+ title,
80
+ icon,
81
+ },
82
+ resources: [explorerResource],
83
+ });
84
+ }
@@ -0,0 +1,2 @@
1
+ export { createDataViewModule } from "./create-data-view-module";
2
+ export type { DataViewModuleConfig } from "./types";
@@ -0,0 +1,42 @@
1
+ import type { ReactNode } from "react";
2
+ import type { TableMetadataMap } from "../generator/metadata-generator";
3
+ import type { SavedViewStore, SavedViewStoreFactory } from "../store/types";
4
+
5
+ /**
6
+ * Configuration for creating a DataView module
7
+ */
8
+ export interface DataViewModuleConfig {
9
+ /** Table metadata (required) */
10
+ tableMetadata: TableMetadataMap;
11
+
12
+ /** GraphQL API URI (required) */
13
+ appUri: string;
14
+
15
+ /** Saved view store implementation (required) */
16
+ store: SavedViewStore | SavedViewStoreFactory;
17
+
18
+ /** Routing configuration */
19
+ path?: {
20
+ /** Module base path (default: "data-view") */
21
+ base?: string;
22
+ /** Explorer resource path (default: "explorer") */
23
+ explorer?: string;
24
+ };
25
+
26
+ /** Module meta information */
27
+ meta?: {
28
+ /** Module title (default: "Data Viewer") */
29
+ title?: string;
30
+ /** Module icon (default: <Database />) */
31
+ icon?: ReactNode;
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Type guard to check if store is a factory function
37
+ */
38
+ export function isStoreFactory(
39
+ store: SavedViewStore | SavedViewStoreFactory,
40
+ ): store is SavedViewStoreFactory {
41
+ return typeof store === "function";
42
+ }
@@ -1,6 +1,6 @@
1
1
  import { Columns3 } from "lucide-react";
2
- import { Checkbox } from "../ui/checkbox";
3
- import { Button } from "../ui/button";
2
+ import { Checkbox } from "./ui/checkbox";
3
+ import { Button } from "./ui/button";
4
4
  import {
5
5
  DropdownMenu,
6
6
  DropdownMenuTrigger,
@@ -10,13 +10,13 @@ import {
10
10
  DropdownMenuSubContent,
11
11
  DropdownMenuSeparator,
12
12
  DropdownMenuLabel,
13
- } from "../ui/dropdown-menu";
13
+ } from "./ui/dropdown-menu";
14
14
  import type {
15
15
  FieldMetadata,
16
16
  RelationMetadata,
17
17
  TableMetadataMap,
18
18
  ExpandedRelationFields,
19
- } from "../types/table-metadata";
19
+ } from "../generator/metadata-generator";
20
20
 
21
21
  interface ColumnSelectorProps {
22
22
  fields: FieldMetadata[];
@@ -15,14 +15,14 @@ import {
15
15
  TableHead,
16
16
  TableHeader,
17
17
  TableRow,
18
- } from "../ui/table";
18
+ } from "./ui/table";
19
19
  import type {
20
20
  FieldMetadata,
21
21
  TableMetadata,
22
22
  RelationMetadata,
23
23
  TableMetadataMap,
24
24
  ExpandedRelationFields,
25
- } from "../types/table-metadata";
25
+ } from "../generator/metadata-generator";
26
26
  import { formatFieldValue } from "../utils/query-builder";
27
27
  import type { SortState } from "./hooks/use-table-data";
28
28
  import { useRelationData } from "./hooks/use-relation-data";
@@ -1,12 +1,12 @@
1
1
  import { useState, useCallback, useMemo } from "react";
2
2
  import { RefreshCw, Download } from "lucide-react";
3
- import { Alert, AlertDescription } from "../ui/alert";
4
- import { Button } from "../ui/button";
3
+ import { Alert, AlertDescription } from "./ui/alert";
4
+ import { Button } from "./ui/button";
5
5
  import type {
6
6
  TableMetadata,
7
7
  TableMetadataMap,
8
8
  FieldMetadata,
9
- } from "../types/table-metadata";
9
+ } from "../generator/metadata-generator";
10
10
  import { formatFieldValue } from "../utils/query-builder";
11
11
  import { TableSelector } from "./table-selector";
12
12
  import { ColumnSelector } from "./column-selector";