nuxt-google-sheets-import 0.1.4 → 0.1.7
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 +110 -123
- package/dist/module.d.mts +0 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +21 -18
- package/dist/runtime/components/GoogleSheetsImportExecute.vue +3 -0
- package/dist/runtime/components/GoogleSheetsImportExport.d.vue.ts +3 -0
- package/dist/runtime/components/GoogleSheetsImportExport.vue +196 -0
- package/dist/runtime/components/GoogleSheetsImportExport.vue.d.ts +3 -0
- package/dist/runtime/components/GoogleSheetsImportSchemaGuide.vue +29 -48
- package/dist/runtime/components/GoogleSheetsImportSource.vue +3 -0
- package/dist/runtime/pages/google-sheets-import.vue +34 -3
- package/dist/runtime/server/api/collection-type.get.js +1 -2
- package/dist/runtime/server/api/schema-columns.get.js +5 -6
- package/dist/runtime/server/api/values.post.js +2 -3
- package/dist/runtime/server/api/write.post.js +2 -3
- package/dist/runtime/server/utils/collectionType.d.ts +1 -1
- package/dist/runtime/server/utils/collectionType.js +39 -30
- package/dist/runtime/server/utils/schemaColumns.js +5 -2
- package/dist/runtime/server/utils/transform.js +1 -42
- package/dist/runtime/utils/clipboard.d.ts +15 -0
- package/dist/runtime/utils/clipboard.js +12 -0
- package/dist/runtime/utils/delimited.d.ts +2 -0
- package/dist/runtime/utils/delimited.js +9 -0
- package/dist/runtime/utils/pathMapping.d.ts +4 -0
- package/dist/runtime/utils/pathMapping.js +86 -0
- package/package.json +1 -1
- package/dist/runtime/import/schemas.d.ts +0 -67
- package/dist/runtime/import/schemas.js +0 -35
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nuxt-google-sheets-import
|
|
2
2
|
|
|
3
3
|
Schema-driven Google Sheets importer for Nuxt Content.
|
|
4
4
|
|
|
5
5
|
## Status
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Nuxt module for schema-driven Google Sheets import/export workflows with Nuxt Content.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -13,107 +13,63 @@ This package scaffold is extracted from a working local module and is ready for
|
|
|
13
13
|
- Writes frontmatter markdown, JSON, or YAML output
|
|
14
14
|
- Supports overwrite strategies (`overwrite`, `skip`, `overwrite-frontmatter`)
|
|
15
15
|
- Exposes UI components and composables for import workflow
|
|
16
|
+
- Includes a built-in `/google-sheets-import` page with tabbed setup/import/export flow
|
|
17
|
+
- Supports TSV clipboard copy for Google Sheets-friendly paste
|
|
16
18
|
|
|
17
|
-
## Install
|
|
19
|
+
## Install
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
|
-
pnpm add
|
|
22
|
+
pnpm add nuxt-google-sheets-import
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
##
|
|
25
|
+
## Configuration
|
|
24
26
|
|
|
25
27
|
```ts
|
|
26
28
|
// nuxt.config.ts
|
|
27
29
|
export default defineNuxtConfig({
|
|
28
|
-
modules: ['
|
|
30
|
+
modules: ['nuxt-google-sheets-import'],
|
|
29
31
|
googleSheetsImport: {
|
|
30
32
|
apiBase: '/api/google-sheets-import',
|
|
31
33
|
googleApiKeyRuntimeKey: 'googleApiKey',
|
|
32
|
-
|
|
33
|
-
schemaRegistryExport: 'schemas',
|
|
34
|
-
defaultContentDir: 'content',
|
|
35
|
-
collectionTypeBySchema: {
|
|
36
|
-
machines: 'page',
|
|
37
|
-
materials: 'data'
|
|
38
|
-
}
|
|
34
|
+
defaultContentDir: 'content/data'
|
|
39
35
|
}
|
|
40
36
|
})
|
|
41
37
|
```
|
|
42
38
|
|
|
39
|
+
Collection type (`page` vs `data`) is derived from your Nuxt Content `content.config.ts` collections.
|
|
40
|
+
|
|
43
41
|
## Environment
|
|
44
42
|
|
|
45
43
|
```bash
|
|
46
44
|
NUXT_GOOGLE_API_KEY=your_google_sheets_api_key
|
|
47
45
|
```
|
|
48
46
|
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
This module currently reads Google Sheets using an API key, so the sheet must be publicly readable.
|
|
47
|
+
## Quick Start
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
1. Add the module and runtime env var.
|
|
50
|
+
2. Define your Zod `schemas` export in `~/utils/googleSheetImportSchemas.ts`.
|
|
51
|
+
3. Ensure matching Nuxt Content collections exist in `content.config.ts`.
|
|
52
|
+
4. Start dev server and open `/google-sheets-import`.
|
|
53
|
+
5. Use tabs in order:
|
|
54
|
+
- `Setup Google Sheet`: confirm schema headers and copy as needed.
|
|
55
|
+
- `Import data`: load sheet rows and write content files.
|
|
56
|
+
- `Export data`: query existing records, copy TSV for Sheets, or download CSV.
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
1. Open your sheet in Google Sheets.
|
|
60
|
-
2. Click `Share`.
|
|
61
|
-
3. Under `General access`, set to `Anyone with the link`.
|
|
62
|
-
4. Set role to `Viewer`.
|
|
63
|
-
5. Copy the spreadsheet ID from the URL:
|
|
64
|
-
- `https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit`
|
|
65
|
-
|
|
66
|
-
If your Google Workspace policy blocks public link sharing, API key access will fail. In that case you need OAuth/service-account based auth (not part of this module yet).
|
|
67
|
-
|
|
68
|
-
### 2) Create API key (Google Cloud Console)
|
|
69
|
-
|
|
70
|
-
1. Open Google Cloud Console: https://console.cloud.google.com/
|
|
71
|
-
2. Create/select a project.
|
|
72
|
-
3. Enable Google Sheets API:
|
|
73
|
-
- https://console.cloud.google.com/apis/library/sheets.googleapis.com
|
|
74
|
-
4. Create credentials (API key):
|
|
75
|
-
- https://console.cloud.google.com/apis/credentials
|
|
76
|
-
- Click `Create credentials` → `API key`
|
|
77
|
-
5. Restrict the key (recommended):
|
|
78
|
-
- **API restrictions**: `Restrict key` → select `Google Sheets API`
|
|
79
|
-
- **Application restrictions**:
|
|
80
|
-
- Server usage: `IP addresses` (recommended for backend)
|
|
81
|
-
- Browser-only usage: `HTTP referrers` (if applicable)
|
|
82
|
-
6. Put the key into `NUXT_GOOGLE_API_KEY`.
|
|
83
|
-
|
|
84
|
-
### 3) Quick verification
|
|
85
|
-
|
|
86
|
-
Call your module endpoint with a known sheet ID and confirm it returns tab metadata:
|
|
58
|
+
Quick endpoint check:
|
|
87
59
|
|
|
88
60
|
`GET /api/google-sheets-import/sheets?spreadsheetId=<SPREADSHEET_ID>`
|
|
89
61
|
|
|
90
|
-
### 4) Troubleshooting (common errors)
|
|
91
|
-
|
|
92
|
-
- `403 PERMISSION_DENIED` / `The caller does not have permission`
|
|
93
|
-
- The sheet is not publicly readable with link.
|
|
94
|
-
- Fix: set `Share` → `General access` → `Anyone with the link` + `Viewer`.
|
|
95
|
-
|
|
96
|
-
- `403 API key not valid` or `API has not been used in project`
|
|
97
|
-
- The key is wrong, restricted to the wrong API, or Sheets API is not enabled.
|
|
98
|
-
- Fix: enable `Google Sheets API` and ensure key restriction includes it.
|
|
99
|
-
|
|
100
|
-
- `403 Requests from this referrer/IP are blocked`
|
|
101
|
-
- Your key application restrictions do not match where requests come from.
|
|
102
|
-
- Fix: update key restrictions (`IP addresses` for server use is preferred).
|
|
103
|
-
|
|
104
|
-
- `404 Requested entity was not found`
|
|
105
|
-
- Spreadsheet ID is incorrect or malformed.
|
|
106
|
-
- Fix: copy ID from `https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit`.
|
|
107
|
-
|
|
108
|
-
- `400 Unable to parse range`
|
|
109
|
-
- Invalid A1 range (for example typo in sheet tab or columns).
|
|
110
|
-
- Fix: verify tab name and use ranges like `A:Z`.
|
|
111
|
-
|
|
112
62
|
## Exported runtime
|
|
113
63
|
|
|
114
|
-
- Components: `GoogleSheetsImportSource`, `GoogleSheetsImportExecute`, `GoogleSheetsImportSchemaGuide`
|
|
64
|
+
- Components: `GoogleSheetsImportSource`, `GoogleSheetsImportExecute`, `GoogleSheetsImportSchemaGuide`, `GoogleSheetsImportExport`
|
|
115
65
|
- Composables: `useGoogleSheetsImport`, `useGoogleSheetsImportWorkflow`
|
|
116
66
|
|
|
67
|
+
The module adds a route at `/google-sheets-import` with `UTabs` for:
|
|
68
|
+
|
|
69
|
+
- `Setup Google Sheet` (schema guide)
|
|
70
|
+
- `Import data` (sheet source/import flow)
|
|
71
|
+
- `Export data` (export existing content records)
|
|
72
|
+
|
|
117
73
|
### Schema helper component
|
|
118
74
|
|
|
119
75
|
Use `GoogleSheetsImportSchemaGuide` to let editors choose a schema and see the expected Google Sheet column headers.
|
|
@@ -126,23 +82,34 @@ Benefits:
|
|
|
126
82
|
|
|
127
83
|
- Reduces import failures by giving editors exact header names before filling a sheet
|
|
128
84
|
- Supports nested/array header patterns used by schema mapping (for example `items[0].name`)
|
|
85
|
+
- Uses a single column for arrays of scalar values (for example `tags` with `foo, bar, baz`)
|
|
129
86
|
- For `page` collections, shows Nuxt Content built-in page override fields and allows copying them separately
|
|
130
87
|
- Supports two copy modes:
|
|
131
88
|
- line-by-line copy
|
|
132
|
-
-
|
|
89
|
+
- tab-separated row copy (pastes horizontally into Google Sheets)
|
|
133
90
|
|
|
134
91
|
Optional prop:
|
|
135
92
|
|
|
136
93
|
- `initialSchema?: string`
|
|
137
94
|
|
|
138
|
-
|
|
95
|
+
### Suggested Cell Examples
|
|
139
96
|
|
|
140
|
-
|
|
97
|
+
Use these value patterns when filling sheets:
|
|
141
98
|
|
|
142
|
-
- `
|
|
143
|
-
- `
|
|
99
|
+
- `string`: `example text`
|
|
100
|
+
- `number`: `123`
|
|
101
|
+
- `boolean`: `true` or `false`
|
|
102
|
+
- `enum` / `literal`: use one of the schema's allowed values
|
|
103
|
+
- `date-like string`: `2026-01-01`
|
|
104
|
+
- `string[]` (scalar array): `foo, bar, baz` in a single cell
|
|
105
|
+
- `object[]` (array of objects): use indexed headers like `items[0].name`, `items[0].price`
|
|
144
106
|
|
|
145
|
-
|
|
107
|
+
## Schema Source
|
|
108
|
+
|
|
109
|
+
Define a `schemas` export in `~/utils/googleSheetImportSchemas.ts`.
|
|
110
|
+
The module auto-imports this as `googleSheetsImportSchemas` for app and server runtime use.
|
|
111
|
+
|
|
112
|
+
Example:
|
|
146
113
|
|
|
147
114
|
```ts
|
|
148
115
|
export const schemas = {
|
|
@@ -151,39 +118,12 @@ export const schemas = {
|
|
|
151
118
|
}
|
|
152
119
|
```
|
|
153
120
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
```ts
|
|
157
|
-
export default defineNuxtConfig({
|
|
158
|
-
modules: ['@tribeweb/nuxt-google-sheets-import'],
|
|
159
|
-
googleSheetsImport: {
|
|
160
|
-
schemaRegistryImport: '~/server/google-sheets/schemas',
|
|
161
|
-
schemaRegistryExport: 'schemas'
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## Playground smoke test (`values` + `write`)
|
|
121
|
+
## Export Behavior
|
|
167
122
|
|
|
168
|
-
|
|
123
|
+
- `Copy for Google Sheets` in `GoogleSheetsImportExport` copies **TSV** to the clipboard for reliable Sheets paste.
|
|
124
|
+
- `Download .csv` provides quoted CSV output for file-based workflows.
|
|
169
125
|
|
|
170
|
-
|
|
171
|
-
- `POST /api/google-sheets-import/values-smoke` (local transform/validation payload)
|
|
172
|
-
- `playground/scripts/smoke.mjs` (calls `values-smoke`, then module `write` for `frontmatter`, `json`, `yaml`)
|
|
173
|
-
|
|
174
|
-
Run with Nuxt dev server active:
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
pnpm --dir packages/nuxt-google-sheets-import smoke:playground
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Optional custom base URL:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
SMOKE_BASE_URL=http://localhost:3000 pnpm --dir packages/nuxt-google-sheets-import smoke:playground
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
## Additional API endpoint
|
|
126
|
+
## Additional API Endpoint
|
|
187
127
|
|
|
188
128
|
- `GET {apiBase}/schema-columns`
|
|
189
129
|
- Query: `schema?`
|
|
@@ -193,31 +133,78 @@ SMOKE_BASE_URL=http://localhost:3000 pnpm --dir packages/nuxt-google-sheets-impo
|
|
|
193
133
|
- `collectionType`: `page | data | unknown`
|
|
194
134
|
- `pageOverrideColumns`: Nuxt Content page override fields (when `collectionType === 'page'`)
|
|
195
135
|
|
|
136
|
+
## Google Setup (Permissions + API Key)
|
|
137
|
+
|
|
138
|
+
This module reads Google Sheets using an API key, so the sheet must be publicly readable.
|
|
139
|
+
|
|
140
|
+
> Security note:
|
|
141
|
+
> API-key access is best suited to non-sensitive sheets that are intentionally shared as `Anyone with the link`.
|
|
142
|
+
> For private or sensitive spreadsheets, prefer OAuth 2.0 or a service account flow instead of API-key access.
|
|
143
|
+
|
|
144
|
+
### 1) Set sheet permissions (Google Sheets)
|
|
145
|
+
|
|
146
|
+
1. Open your sheet in Google Sheets.
|
|
147
|
+
2. Click `Share`.
|
|
148
|
+
3. Under `General access`, set to `Anyone with the link`.
|
|
149
|
+
4. Set role to `Viewer`.
|
|
150
|
+
5. Copy the spreadsheet ID from the URL:
|
|
151
|
+
- `https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit`
|
|
152
|
+
|
|
153
|
+
If your Google Workspace policy blocks public link sharing, API key access will fail. In that case you need OAuth/service-account based auth (not part of this module yet).
|
|
154
|
+
|
|
155
|
+
### 2) Create API key (Google Cloud Console)
|
|
156
|
+
|
|
157
|
+
1. Open Google Cloud Console: https://console.cloud.google.com/
|
|
158
|
+
2. Create/select a project.
|
|
159
|
+
3. Enable Google Sheets API:
|
|
160
|
+
- https://console.cloud.google.com/apis/library/sheets.googleapis.com
|
|
161
|
+
4. Create credentials (API key):
|
|
162
|
+
- https://console.cloud.google.com/apis/credentials
|
|
163
|
+
- Click `Create credentials` -> `API key`
|
|
164
|
+
5. Restrict the key (recommended):
|
|
165
|
+
- **API restrictions**: `Restrict key` -> select `Google Sheets API`
|
|
166
|
+
- **Application restrictions**:
|
|
167
|
+
- Server usage: `IP addresses` (recommended for backend)
|
|
168
|
+
- Browser-only usage: `HTTP referrers` (if applicable)
|
|
169
|
+
6. Put the key into `NUXT_GOOGLE_API_KEY`.
|
|
170
|
+
|
|
171
|
+
### 3) Troubleshooting (common errors)
|
|
172
|
+
|
|
173
|
+
- `403 PERMISSION_DENIED` / `The caller does not have permission`
|
|
174
|
+
- The sheet is not publicly readable with link.
|
|
175
|
+
- Fix: set `Share` -> `General access` -> `Anyone with the link` + `Viewer`.
|
|
176
|
+
|
|
177
|
+
- `403 API key not valid` or `API has not been used in project`
|
|
178
|
+
- The key is wrong, restricted to the wrong API, or Sheets API is not enabled.
|
|
179
|
+
- Fix: enable `Google Sheets API` and ensure key restriction includes it.
|
|
180
|
+
|
|
181
|
+
- `403 Requests from this referrer/IP are blocked`
|
|
182
|
+
- Your key application restrictions do not match where requests come from.
|
|
183
|
+
- Fix: update key restrictions (`IP addresses` for server use is preferred).
|
|
184
|
+
|
|
185
|
+
- `404 Requested entity was not found`
|
|
186
|
+
- Spreadsheet ID is incorrect or malformed.
|
|
187
|
+
- Fix: copy ID from `https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit`.
|
|
188
|
+
|
|
189
|
+
- `400 Unable to parse range`
|
|
190
|
+
- Invalid A1 range (for example typo in sheet tab or columns).
|
|
191
|
+
- Fix: verify tab name and use ranges like `A:Z`.
|
|
192
|
+
|
|
196
193
|
## Publish checklist
|
|
197
194
|
|
|
198
195
|
- Add playground integration tests for `/values` and `/write`
|
|
199
196
|
- Add CI (`lint`, `typecheck`, `build`) and release workflow
|
|
200
197
|
- Verify Nuxt 4 peer compatibility matrix
|
|
201
198
|
|
|
202
|
-
##
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
pnpm --dir packages/nuxt-google-sheets-import release:check
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Then authenticate and publish:
|
|
199
|
+
## Release
|
|
209
200
|
|
|
210
201
|
```bash
|
|
211
|
-
npm
|
|
212
|
-
pnpm --dir packages/nuxt-google-sheets-import publish --access public
|
|
202
|
+
npm run release
|
|
213
203
|
```
|
|
214
204
|
|
|
215
|
-
|
|
205
|
+
If needed, authenticate and publish manually:
|
|
216
206
|
|
|
217
207
|
```bash
|
|
218
208
|
npm login
|
|
219
|
-
|
|
220
|
-
# or: release:minor / release:major
|
|
209
|
+
npm publish --access public
|
|
221
210
|
```
|
|
222
|
-
|
|
223
|
-
These scripts use `npm version --no-git-tag-version`, so they update `package.json` version without creating a git tag/commit.
|
package/dist/module.d.mts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, extendPages, addServerHandler, addImportsDir, addComponentsDir, addImports } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, extendPages, addServerHandler, addImportsDir, addComponentsDir, addImports, addServerImports } from '@nuxt/kit';
|
|
2
2
|
|
|
3
3
|
const module$1 = defineNuxtModule({
|
|
4
4
|
meta: {
|
|
@@ -6,6 +6,9 @@ const module$1 = defineNuxtModule({
|
|
|
6
6
|
configKey: "googleSheetsImport"
|
|
7
7
|
},
|
|
8
8
|
moduleDependencies: {
|
|
9
|
+
"@nuxt/content": {
|
|
10
|
+
version: ">=3"
|
|
11
|
+
},
|
|
9
12
|
"@nuxt/ui": {
|
|
10
13
|
version: ">=4"
|
|
11
14
|
}
|
|
@@ -13,32 +16,20 @@ const module$1 = defineNuxtModule({
|
|
|
13
16
|
defaults: {
|
|
14
17
|
apiBase: "/api/google-sheets-import",
|
|
15
18
|
googleApiKeyRuntimeKey: "googleApiKey",
|
|
16
|
-
defaultContentDir: "content/data"
|
|
17
|
-
collectionTypeBySchema: {}
|
|
18
|
-
// schemaRegistryImport: '#imports',
|
|
19
|
-
// schemaRegistryExport: 'schemas',
|
|
19
|
+
defaultContentDir: "content/data"
|
|
20
20
|
},
|
|
21
21
|
setup(options, nuxt) {
|
|
22
22
|
const resolver = createResolver(import.meta.url);
|
|
23
|
-
const normalizedCollectionTypeBySchema = Object.entries(options.collectionTypeBySchema).reduce((acc, [key, value]) => {
|
|
24
|
-
const trimmed = key.trim();
|
|
25
|
-
acc[trimmed] = value;
|
|
26
|
-
acc[trimmed.toLowerCase()] = value;
|
|
27
|
-
acc[trimmed.replace(/[-\s]+/g, "_").toLowerCase()] = value;
|
|
28
|
-
return acc;
|
|
29
|
-
}, {});
|
|
30
23
|
nuxt.options.runtimeConfig.googleSheetsImport = {
|
|
24
|
+
...nuxt.options.runtimeConfig.googleSheetsImport,
|
|
31
25
|
apiBase: options.apiBase,
|
|
32
26
|
googleApiKeyRuntimeKey: options.googleApiKeyRuntimeKey,
|
|
33
|
-
defaultContentDir: options.defaultContentDir
|
|
34
|
-
collectionTypeBySchema: normalizedCollectionTypeBySchema
|
|
35
|
-
// schemaRegistryImport: options.schemaRegistryImport,
|
|
36
|
-
// schemaRegistryExport: options.schemaRegistryExport,
|
|
27
|
+
defaultContentDir: options.defaultContentDir
|
|
37
28
|
};
|
|
38
29
|
nuxt.options.runtimeConfig.public.googleSheetsImport = {
|
|
30
|
+
...nuxt.options.runtimeConfig.public.googleSheetsImport,
|
|
39
31
|
apiBase: options.apiBase,
|
|
40
|
-
defaultContentDir: options.defaultContentDir
|
|
41
|
-
collectionTypeBySchema: normalizedCollectionTypeBySchema
|
|
32
|
+
defaultContentDir: options.defaultContentDir
|
|
42
33
|
};
|
|
43
34
|
nuxt.options.css.push(resolver.resolve("./runtime/assets/css/main.css"));
|
|
44
35
|
extendPages((pages) => {
|
|
@@ -78,6 +69,11 @@ const module$1 = defineNuxtModule({
|
|
|
78
69
|
path: resolver.resolve("./runtime/components")
|
|
79
70
|
});
|
|
80
71
|
addImports([
|
|
72
|
+
{
|
|
73
|
+
from: "~/utils/googleSheetImportSchemas",
|
|
74
|
+
name: "schemas",
|
|
75
|
+
as: "googleSheetsImportSchemas"
|
|
76
|
+
},
|
|
81
77
|
{
|
|
82
78
|
from: resolver.resolve("./runtime/types/googleSheetsApi"),
|
|
83
79
|
name: "GoogleSheetsApiValues",
|
|
@@ -109,6 +105,13 @@ const module$1 = defineNuxtModule({
|
|
|
109
105
|
type: true
|
|
110
106
|
}
|
|
111
107
|
]);
|
|
108
|
+
addServerImports([
|
|
109
|
+
{
|
|
110
|
+
from: "~/utils/googleSheetImportSchemas",
|
|
111
|
+
name: "schemas",
|
|
112
|
+
as: "googleSheetsImportSchemas"
|
|
113
|
+
}
|
|
114
|
+
]);
|
|
112
115
|
}
|
|
113
116
|
});
|
|
114
117
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
import { googleSheetsImportSchemas, queryCollection, useAsyncData, useToast } from "#imports";
|
|
4
|
+
import { copyTextWithSuccessToast } from "../utils/clipboard";
|
|
5
|
+
import { toCsvRow, toTsvRow } from "../utils/delimited";
|
|
6
|
+
import { flattenRecordToStringMap } from "../utils/pathMapping";
|
|
7
|
+
const toast = useToast();
|
|
8
|
+
const queryCollectionLoose = queryCollection;
|
|
9
|
+
const selectedSchema = ref("");
|
|
10
|
+
const {
|
|
11
|
+
data: rowsData,
|
|
12
|
+
pending,
|
|
13
|
+
error: loadError,
|
|
14
|
+
status,
|
|
15
|
+
execute,
|
|
16
|
+
clear
|
|
17
|
+
} = useAsyncData(
|
|
18
|
+
"google-sheets-import-export-records",
|
|
19
|
+
async () => {
|
|
20
|
+
if (!selectedSchema.value) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const result = await queryCollectionLoose(selectedSchema.value).all();
|
|
24
|
+
return result;
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
immediate: false,
|
|
28
|
+
default: () => []
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
const rows = computed(() => rowsData.value ?? []);
|
|
32
|
+
const error = computed(() => loadError.value?.message ?? "");
|
|
33
|
+
watch(selectedSchema, () => {
|
|
34
|
+
clear();
|
|
35
|
+
});
|
|
36
|
+
const schemaMap = computed(() => googleSheetsImportSchemas ?? {});
|
|
37
|
+
const schemaOptions = computed(() => Object.keys(schemaMap.value).sort((left, right) => left.localeCompare(right)).map((schema) => ({ label: schema, value: schema })));
|
|
38
|
+
const headers = computed(() => {
|
|
39
|
+
const all = /* @__PURE__ */ new Set();
|
|
40
|
+
for (const row of rows.value) {
|
|
41
|
+
flattenRecordToStringMap(row).forEach((_, key) => all.add(key));
|
|
42
|
+
}
|
|
43
|
+
return Array.from(all).sort((left, right) => left.localeCompare(right));
|
|
44
|
+
});
|
|
45
|
+
const csvText = computed(() => {
|
|
46
|
+
if (!headers.value.length) {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
const lines = [];
|
|
50
|
+
lines.push(toCsvRow(headers.value));
|
|
51
|
+
for (const row of rows.value) {
|
|
52
|
+
const flat = flattenRecordToStringMap(row);
|
|
53
|
+
const values = headers.value.map((header) => flat.get(header) ?? "");
|
|
54
|
+
lines.push(toCsvRow(values));
|
|
55
|
+
}
|
|
56
|
+
return `${lines.join("\n")}
|
|
57
|
+
`;
|
|
58
|
+
});
|
|
59
|
+
const tsvText = computed(() => {
|
|
60
|
+
if (!headers.value.length) {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
const lines = [];
|
|
64
|
+
lines.push(toTsvRow(headers.value));
|
|
65
|
+
for (const row of rows.value) {
|
|
66
|
+
const flat = flattenRecordToStringMap(row);
|
|
67
|
+
const values = headers.value.map((header) => flat.get(header) ?? "");
|
|
68
|
+
lines.push(toTsvRow(values));
|
|
69
|
+
}
|
|
70
|
+
return `${lines.join("\n")}
|
|
71
|
+
`;
|
|
72
|
+
});
|
|
73
|
+
const previewText = computed(() => {
|
|
74
|
+
if (!csvText.value) {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
return csvText.value.split("\n").slice(0, 11).join("\n");
|
|
78
|
+
});
|
|
79
|
+
async function loadRecords() {
|
|
80
|
+
if (!selectedSchema.value) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await execute();
|
|
84
|
+
if (status.value === "success") {
|
|
85
|
+
toast.add({
|
|
86
|
+
title: "Export ready",
|
|
87
|
+
description: `Loaded ${rows.value.length} row(s) from collection ${selectedSchema.value}.`,
|
|
88
|
+
color: "success"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function copyCsv() {
|
|
93
|
+
await copyTextWithSuccessToast({
|
|
94
|
+
text: tsvText.value,
|
|
95
|
+
toast,
|
|
96
|
+
description: "Tab-separated rows copied for Google Sheets paste.",
|
|
97
|
+
title: "Copied"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function downloadCsv() {
|
|
101
|
+
if (!csvText.value) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const blob = new Blob([csvText.value], { type: "text/csv;charset=utf-8" });
|
|
105
|
+
const url = URL.createObjectURL(blob);
|
|
106
|
+
const link = document.createElement("a");
|
|
107
|
+
link.href = url;
|
|
108
|
+
link.download = `${selectedSchema.value || "export"}.csv`;
|
|
109
|
+
link.click();
|
|
110
|
+
URL.revokeObjectURL(url);
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<div class="space-y-4">
|
|
116
|
+
<UAlert
|
|
117
|
+
title="Export Existing Content"
|
|
118
|
+
description="Load records from a Nuxt Content collection and export them as CSV for Google Sheets."
|
|
119
|
+
color="neutral"
|
|
120
|
+
variant="subtle"
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
<UAlert
|
|
124
|
+
v-if="error"
|
|
125
|
+
title="Could not load collection"
|
|
126
|
+
:description="error"
|
|
127
|
+
color="error"
|
|
128
|
+
variant="subtle"
|
|
129
|
+
/>
|
|
130
|
+
|
|
131
|
+
<UFormField
|
|
132
|
+
label="Collection schema"
|
|
133
|
+
name="schema"
|
|
134
|
+
description="Pick the schema/collection to export."
|
|
135
|
+
>
|
|
136
|
+
<USelect
|
|
137
|
+
v-model="selectedSchema"
|
|
138
|
+
:items="schemaOptions"
|
|
139
|
+
value-key="value"
|
|
140
|
+
class="w-full max-w-sm"
|
|
141
|
+
icon="i-heroicons-cube-20-solid"
|
|
142
|
+
/>
|
|
143
|
+
</UFormField>
|
|
144
|
+
|
|
145
|
+
<div class="flex flex-wrap gap-2">
|
|
146
|
+
<UButton
|
|
147
|
+
label="Load records"
|
|
148
|
+
icon="i-heroicons-arrow-down-tray-20-solid"
|
|
149
|
+
:disabled="!selectedSchema"
|
|
150
|
+
:loading="pending"
|
|
151
|
+
@click="loadRecords"
|
|
152
|
+
/>
|
|
153
|
+
<UButton
|
|
154
|
+
label="Copy for Google Sheets"
|
|
155
|
+
icon="i-heroicons-clipboard-document-20-solid"
|
|
156
|
+
color="neutral"
|
|
157
|
+
variant="subtle"
|
|
158
|
+
:disabled="!tsvText"
|
|
159
|
+
@click="copyCsv"
|
|
160
|
+
/>
|
|
161
|
+
<UButton
|
|
162
|
+
label="Download .csv"
|
|
163
|
+
icon="i-heroicons-document-arrow-down-20-solid"
|
|
164
|
+
color="neutral"
|
|
165
|
+
variant="subtle"
|
|
166
|
+
:disabled="!csvText"
|
|
167
|
+
@click="downloadCsv"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<UAlert
|
|
172
|
+
v-if="status === 'success'"
|
|
173
|
+
title="Export prepared"
|
|
174
|
+
:description="`${rows.length} row(s), ${headers.length} column(s).`"
|
|
175
|
+
color="success"
|
|
176
|
+
variant="subtle"
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
<UCollapsible
|
|
180
|
+
v-if="previewText"
|
|
181
|
+
class="flex flex-col gap-2 w-full"
|
|
182
|
+
>
|
|
183
|
+
<UButton
|
|
184
|
+
label="Preview CSV (first 10 rows)"
|
|
185
|
+
color="neutral"
|
|
186
|
+
variant="subtle"
|
|
187
|
+
trailing-icon="i-lucide-chevron-down"
|
|
188
|
+
block
|
|
189
|
+
/>
|
|
190
|
+
|
|
191
|
+
<template #content>
|
|
192
|
+
<pre class="text-xs whitespace-pre-wrap">{{ previewText }}</pre>
|
|
193
|
+
</template>
|
|
194
|
+
</UCollapsible>
|
|
195
|
+
</div>
|
|
196
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|