@izumisy-tailor/tailor-data-viewer 0.1.4 → 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.
- package/README.md +44 -4
- package/dist/generator/index.d.mts +7 -1
- package/docs/app-shell-module.md +151 -0
- package/docs/saved-view-store.md +155 -0
- package/package.json +27 -2
- package/src/app-shell/create-data-view-module.tsx +84 -0
- package/src/app-shell/index.ts +2 -0
- package/src/app-shell/types.ts +42 -0
- package/src/component/column-selector.tsx +4 -4
- package/src/component/data-table.tsx +2 -2
- package/src/component/data-view-tab-content.tsx +3 -3
- package/src/component/data-viewer.tsx +38 -9
- package/src/component/hooks/use-accessible-tables.ts +1 -1
- package/src/component/hooks/use-column-state.ts +1 -1
- package/src/component/hooks/use-relation-data.ts +1 -1
- package/src/component/hooks/use-table-access-check.ts +103 -0
- package/src/component/hooks/use-table-data.ts +1 -1
- package/src/component/index.ts +4 -1
- package/src/component/pagination.tsx +1 -1
- package/src/component/relation-content.tsx +4 -4
- package/src/component/saved-view-context.tsx +195 -48
- package/src/component/search-filter.tsx +8 -8
- package/src/component/single-record-tab-content.tsx +5 -5
- package/src/component/table-selector.tsx +2 -2
- package/src/component/types.ts +1 -1
- package/src/component/view-save-load.tsx +4 -4
- package/src/generator/metadata-generator.ts +7 -0
- package/src/store/indexeddb.ts +150 -0
- package/src/store/tailordb/index.ts +204 -0
- package/src/store/tailordb/schema.ts +114 -0
- package/src/store/types.ts +85 -0
- package/src/utils/query-builder.ts +1 -1
- package/src/types/table-metadata.ts +0 -72
- /package/{API.md → docs/API.md} +0 -0
- /package/src/{lib → component/lib}/utils.ts +0 -0
- /package/src/{ui → component/ui}/alert.tsx +0 -0
- /package/src/{ui → component/ui}/badge.tsx +0 -0
- /package/src/{ui → component/ui}/button.tsx +0 -0
- /package/src/{ui → component/ui}/card.tsx +0 -0
- /package/src/{ui → component/ui}/checkbox.tsx +0 -0
- /package/src/{ui → component/ui}/collapsible.tsx +0 -0
- /package/src/{ui → component/ui}/dialog.tsx +0 -0
- /package/src/{ui → component/ui}/dropdown-menu.tsx +0 -0
- /package/src/{ui → component/ui}/input.tsx +0 -0
- /package/src/{ui → component/ui}/label.tsx +0 -0
- /package/src/{ui → component/ui}/select.tsx +0 -0
- /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
|
-
|
|
37
|
+
There are two ways to use Data Viewer:
|
|
38
38
|
|
|
39
|
-
|
|
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 };
|
|
@@ -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.
|
|
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
|
-
"
|
|
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,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 "
|
|
3
|
-
import { Button } from "
|
|
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 "
|
|
13
|
+
} from "./ui/dropdown-menu";
|
|
14
14
|
import type {
|
|
15
15
|
FieldMetadata,
|
|
16
16
|
RelationMetadata,
|
|
17
17
|
TableMetadataMap,
|
|
18
18
|
ExpandedRelationFields,
|
|
19
|
-
} from "../
|
|
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 "
|
|
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 "../
|
|
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 "
|
|
4
|
-
import { Button } from "
|
|
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 "../
|
|
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";
|