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.
Files changed (28) hide show
  1. package/README.md +110 -123
  2. package/dist/module.d.mts +0 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +21 -18
  5. package/dist/runtime/components/GoogleSheetsImportExecute.vue +3 -0
  6. package/dist/runtime/components/GoogleSheetsImportExport.d.vue.ts +3 -0
  7. package/dist/runtime/components/GoogleSheetsImportExport.vue +196 -0
  8. package/dist/runtime/components/GoogleSheetsImportExport.vue.d.ts +3 -0
  9. package/dist/runtime/components/GoogleSheetsImportSchemaGuide.vue +29 -48
  10. package/dist/runtime/components/GoogleSheetsImportSource.vue +3 -0
  11. package/dist/runtime/pages/google-sheets-import.vue +34 -3
  12. package/dist/runtime/server/api/collection-type.get.js +1 -2
  13. package/dist/runtime/server/api/schema-columns.get.js +5 -6
  14. package/dist/runtime/server/api/values.post.js +2 -3
  15. package/dist/runtime/server/api/write.post.js +2 -3
  16. package/dist/runtime/server/utils/collectionType.d.ts +1 -1
  17. package/dist/runtime/server/utils/collectionType.js +39 -30
  18. package/dist/runtime/server/utils/schemaColumns.js +5 -2
  19. package/dist/runtime/server/utils/transform.js +1 -42
  20. package/dist/runtime/utils/clipboard.d.ts +15 -0
  21. package/dist/runtime/utils/clipboard.js +12 -0
  22. package/dist/runtime/utils/delimited.d.ts +2 -0
  23. package/dist/runtime/utils/delimited.js +9 -0
  24. package/dist/runtime/utils/pathMapping.d.ts +4 -0
  25. package/dist/runtime/utils/pathMapping.js +86 -0
  26. package/package.json +1 -1
  27. package/dist/runtime/import/schemas.d.ts +0 -67
  28. package/dist/runtime/import/schemas.js +0 -35
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # @tribeweb/nuxt-google-sheets-import
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
- This package scaffold is extracted from a working local module and is ready for standalone hardening/publishing.
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 (workspace)
19
+ ## Install
18
20
 
19
21
  ```bash
20
- pnpm add @tribeweb/nuxt-google-sheets-import
22
+ pnpm add nuxt-google-sheets-import
21
23
  ```
22
24
 
23
- ## Configure
25
+ ## Configuration
24
26
 
25
27
  ```ts
26
28
  // nuxt.config.ts
27
29
  export default defineNuxtConfig({
28
- modules: ['@tribeweb/nuxt-google-sheets-import'],
30
+ modules: ['nuxt-google-sheets-import'],
29
31
  googleSheetsImport: {
30
32
  apiBase: '/api/google-sheets-import',
31
33
  googleApiKeyRuntimeKey: 'googleApiKey',
32
- schemaRegistryImport: '#imports',
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
- ## Google setup (permissions + API key)
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
- > Security note:
54
- > API-key access is best suited to non-sensitive sheets that are intentionally shared as `Anyone with the link`.
55
- > For private or sensitive spreadsheets, prefer OAuth 2.0 or a service account flow instead of API-key access.
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
- ### 1) Set sheet permissions (Google Sheets)
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
- - CSV-row copy (pastes horizontally into Google Sheets)
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
- ## Schema registry
95
+ ### Suggested Cell Examples
139
96
 
140
- The module resolves Zod schemas from a configurable module import so it does not depend on app-specific paths.
97
+ Use these value patterns when filling sheets:
141
98
 
142
- - `schemaRegistryImport` (default: `#imports`)
143
- - `schemaRegistryExport` (default: `schemas`)
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
- Expected shape:
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
- If your schemas are exported from a custom file, point the module to it:
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
- The playground includes:
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
- - `playground/server/google-sheets/schemas.ts` (tiny local schema registry)
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
- ## Publish (next step)
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 login
212
- pnpm --dir packages/nuxt-google-sheets-import publish --access public
202
+ npm run release
213
203
  ```
214
204
 
215
- Or use one-command release scripts (bumps version + checks + publishes):
205
+ If needed, authenticate and publish manually:
216
206
 
217
207
  ```bash
218
208
  npm login
219
- pnpm --dir packages/nuxt-google-sheets-import release:patch
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
@@ -4,7 +4,6 @@ interface ModuleOptions {
4
4
  apiBase: string;
5
5
  googleApiKeyRuntimeKey: string;
6
6
  defaultContentDir: string;
7
- collectionTypeBySchema: Record<string, 'page' | 'data'>;
8
7
  }
9
8
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
10
9
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-google-sheets-import",
3
3
  "configKey": "googleSheetsImport",
4
- "version": "0.1.4",
4
+ "version": "0.1.7",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
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
 
@@ -1,4 +1,7 @@
1
1
  <script setup>
2
+ import { useGoogleSheetsImportWorkflow } from "../composables/useGoogleSheetsImportWorkflow";
3
+ import { useToast } from "#imports";
4
+ import { computed } from "vue";
2
5
  const props = defineProps({
3
6
  query: { type: Object, required: true }
4
7
  });
@@ -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;