@aws505/sheetsite 1.0.6 → 1.0.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/dist/export/index.js +225 -0
- package/dist/export/index.js.map +1 -0
- package/dist/export/index.mjs +185 -0
- package/dist/export/index.mjs.map +1 -0
- package/package.json +8 -1
- package/src/export/index.ts +13 -0
- package/src/export/xlsx.ts +277 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/export/index.ts
|
|
31
|
+
var export_exports = {};
|
|
32
|
+
__export(export_exports, {
|
|
33
|
+
exportSiteDataToXlsx: () => exportSiteDataToXlsx,
|
|
34
|
+
generateXlsxBuffer: () => generateXlsxBuffer,
|
|
35
|
+
generateXlsxWorkbook: () => generateXlsxWorkbook,
|
|
36
|
+
writeXlsxFile: () => writeXlsxFile
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(export_exports);
|
|
39
|
+
|
|
40
|
+
// src/export/xlsx.ts
|
|
41
|
+
var XLSX = __toESM(require("xlsx"));
|
|
42
|
+
var BUSINESS_COLUMNS = [
|
|
43
|
+
{ key: "name", header: "name" },
|
|
44
|
+
{ key: "tagline", header: "tagline" },
|
|
45
|
+
{ key: "description", header: "description" },
|
|
46
|
+
{ key: "aboutShort", header: "aboutShort" },
|
|
47
|
+
{ key: "phone", header: "phone" },
|
|
48
|
+
{ key: "email", header: "email" },
|
|
49
|
+
{ key: "addressLine1", header: "addressLine1" },
|
|
50
|
+
{ key: "addressLine2", header: "addressLine2" },
|
|
51
|
+
{ key: "city", header: "city" },
|
|
52
|
+
{ key: "state", header: "state" },
|
|
53
|
+
{ key: "zip", header: "zip" },
|
|
54
|
+
{ key: "timezone", header: "timezone" },
|
|
55
|
+
{ key: "googleMapsUrl", header: "googleMapsUrl" },
|
|
56
|
+
{ key: "heroImageUrl", header: "heroImageUrl" },
|
|
57
|
+
{ key: "logoUrl", header: "logoUrl" },
|
|
58
|
+
{ key: "primaryCtaText", header: "primaryCtaText" },
|
|
59
|
+
{ key: "primaryCtaUrl", header: "primaryCtaUrl" },
|
|
60
|
+
{ key: "socialYelp", header: "socialYelp" },
|
|
61
|
+
{ key: "socialInstagram", header: "socialInstagram" },
|
|
62
|
+
{ key: "socialFacebook", header: "socialFacebook" },
|
|
63
|
+
{ key: "priceRange", header: "priceRange" }
|
|
64
|
+
];
|
|
65
|
+
var HOURS_COLUMNS = [
|
|
66
|
+
{ key: "day", header: "day" },
|
|
67
|
+
{ key: "open", header: "open" },
|
|
68
|
+
{ key: "close", header: "close" },
|
|
69
|
+
{ key: "closed", header: "closed" }
|
|
70
|
+
];
|
|
71
|
+
var SERVICES_COLUMNS = [
|
|
72
|
+
{ key: "id", header: "id" },
|
|
73
|
+
{ key: "name", header: "name" },
|
|
74
|
+
{ key: "title", header: "title" },
|
|
75
|
+
{ key: "description", header: "description" },
|
|
76
|
+
{ key: "priceNote", header: "priceNote" },
|
|
77
|
+
{ key: "price", header: "price" },
|
|
78
|
+
{ key: "duration", header: "duration" },
|
|
79
|
+
{ key: "category", header: "category" },
|
|
80
|
+
{ key: "featured", header: "featured" },
|
|
81
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
82
|
+
];
|
|
83
|
+
var TESTIMONIALS_COLUMNS = [
|
|
84
|
+
{ key: "id", header: "id" },
|
|
85
|
+
{ key: "quote", header: "quote" },
|
|
86
|
+
{ key: "name", header: "name" },
|
|
87
|
+
{ key: "context", header: "context" },
|
|
88
|
+
{ key: "rating", header: "rating" },
|
|
89
|
+
{ key: "source", header: "source" },
|
|
90
|
+
{ key: "featured", header: "featured" },
|
|
91
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
92
|
+
];
|
|
93
|
+
var FAQ_COLUMNS = [
|
|
94
|
+
{ key: "id", header: "id" },
|
|
95
|
+
{ key: "question", header: "question" },
|
|
96
|
+
{ key: "answer", header: "answer" },
|
|
97
|
+
{ key: "category", header: "category" },
|
|
98
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
99
|
+
];
|
|
100
|
+
var GALLERY_COLUMNS = [
|
|
101
|
+
{ key: "id", header: "id" },
|
|
102
|
+
{ key: "url", header: "url" },
|
|
103
|
+
{ key: "imageUrl", header: "imageUrl" },
|
|
104
|
+
{ key: "alt", header: "alt" },
|
|
105
|
+
{ key: "caption", header: "caption" },
|
|
106
|
+
{ key: "category", header: "category" },
|
|
107
|
+
{ key: "featured", header: "featured" },
|
|
108
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
109
|
+
];
|
|
110
|
+
var MENU_COLUMNS = [
|
|
111
|
+
{ key: "id", header: "id" },
|
|
112
|
+
{ key: "name", header: "name" },
|
|
113
|
+
{ key: "description", header: "description" },
|
|
114
|
+
{ key: "price", header: "price" },
|
|
115
|
+
{ key: "priceNote", header: "priceNote" },
|
|
116
|
+
{ key: "category", header: "category" },
|
|
117
|
+
{ key: "featured", header: "featured" },
|
|
118
|
+
{ key: "available", header: "available" },
|
|
119
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
120
|
+
];
|
|
121
|
+
var POPUP_COLUMNS = [
|
|
122
|
+
{ key: "enabled", header: "enabled" },
|
|
123
|
+
{ key: "type", header: "type" },
|
|
124
|
+
{ key: "title", header: "title" },
|
|
125
|
+
{ key: "message", header: "message" },
|
|
126
|
+
{ key: "buttonText", header: "buttonText" },
|
|
127
|
+
{ key: "buttonUrl", header: "buttonUrl" },
|
|
128
|
+
{ key: "delay", header: "delay" }
|
|
129
|
+
];
|
|
130
|
+
function objectToRow(obj, columns) {
|
|
131
|
+
const row = {};
|
|
132
|
+
for (const col of columns) {
|
|
133
|
+
const value = obj[col.key];
|
|
134
|
+
if (typeof value === "boolean") {
|
|
135
|
+
row[col.header] = value ? "TRUE" : "FALSE";
|
|
136
|
+
} else if (value !== void 0 && value !== null) {
|
|
137
|
+
row[col.header] = value;
|
|
138
|
+
} else {
|
|
139
|
+
row[col.header] = "";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return row;
|
|
143
|
+
}
|
|
144
|
+
function arrayToSheetData(items, columns) {
|
|
145
|
+
return items.map((item) => objectToRow(item, columns));
|
|
146
|
+
}
|
|
147
|
+
function generateXlsxWorkbook(data, options = {}) {
|
|
148
|
+
const workbook = XLSX.utils.book_new();
|
|
149
|
+
if (data.business) {
|
|
150
|
+
const businessData = [objectToRow(data.business, BUSINESS_COLUMNS)];
|
|
151
|
+
const businessSheet = XLSX.utils.json_to_sheet(businessData, {
|
|
152
|
+
header: BUSINESS_COLUMNS.map((c) => c.header)
|
|
153
|
+
});
|
|
154
|
+
XLSX.utils.book_append_sheet(workbook, businessSheet, "Business");
|
|
155
|
+
}
|
|
156
|
+
if (data.hours && (data.hours.length > 0 || options.includeEmptyTabs)) {
|
|
157
|
+
const hoursData = arrayToSheetData(data.hours, HOURS_COLUMNS);
|
|
158
|
+
const hoursSheet = XLSX.utils.json_to_sheet(hoursData, {
|
|
159
|
+
header: HOURS_COLUMNS.map((c) => c.header)
|
|
160
|
+
});
|
|
161
|
+
XLSX.utils.book_append_sheet(workbook, hoursSheet, "Hours");
|
|
162
|
+
}
|
|
163
|
+
if (data.services && (data.services.length > 0 || options.includeEmptyTabs)) {
|
|
164
|
+
const servicesData = arrayToSheetData(data.services, SERVICES_COLUMNS);
|
|
165
|
+
const servicesSheet = XLSX.utils.json_to_sheet(servicesData, {
|
|
166
|
+
header: SERVICES_COLUMNS.map((c) => c.header)
|
|
167
|
+
});
|
|
168
|
+
XLSX.utils.book_append_sheet(workbook, servicesSheet, "Services");
|
|
169
|
+
}
|
|
170
|
+
if (data.testimonials && (data.testimonials.length > 0 || options.includeEmptyTabs)) {
|
|
171
|
+
const testimonialsData = arrayToSheetData(data.testimonials, TESTIMONIALS_COLUMNS);
|
|
172
|
+
const testimonialsSheet = XLSX.utils.json_to_sheet(testimonialsData, {
|
|
173
|
+
header: TESTIMONIALS_COLUMNS.map((c) => c.header)
|
|
174
|
+
});
|
|
175
|
+
XLSX.utils.book_append_sheet(workbook, testimonialsSheet, "Testimonials");
|
|
176
|
+
}
|
|
177
|
+
if (data.faq && (data.faq.length > 0 || options.includeEmptyTabs)) {
|
|
178
|
+
const faqData = arrayToSheetData(data.faq, FAQ_COLUMNS);
|
|
179
|
+
const faqSheet = XLSX.utils.json_to_sheet(faqData, {
|
|
180
|
+
header: FAQ_COLUMNS.map((c) => c.header)
|
|
181
|
+
});
|
|
182
|
+
XLSX.utils.book_append_sheet(workbook, faqSheet, "FAQ");
|
|
183
|
+
}
|
|
184
|
+
if (data.gallery && (data.gallery.length > 0 || options.includeEmptyTabs)) {
|
|
185
|
+
const galleryData = arrayToSheetData(data.gallery, GALLERY_COLUMNS);
|
|
186
|
+
const gallerySheet = XLSX.utils.json_to_sheet(galleryData, {
|
|
187
|
+
header: GALLERY_COLUMNS.map((c) => c.header)
|
|
188
|
+
});
|
|
189
|
+
XLSX.utils.book_append_sheet(workbook, gallerySheet, "Gallery");
|
|
190
|
+
}
|
|
191
|
+
if (data.menu && (data.menu.length > 0 || options.includeEmptyTabs)) {
|
|
192
|
+
const menuData = arrayToSheetData(data.menu, MENU_COLUMNS);
|
|
193
|
+
const menuSheet = XLSX.utils.json_to_sheet(menuData, {
|
|
194
|
+
header: MENU_COLUMNS.map((c) => c.header)
|
|
195
|
+
});
|
|
196
|
+
XLSX.utils.book_append_sheet(workbook, menuSheet, "Menu");
|
|
197
|
+
}
|
|
198
|
+
if (data.popup) {
|
|
199
|
+
const popupData = [objectToRow(data.popup, POPUP_COLUMNS)];
|
|
200
|
+
const popupSheet = XLSX.utils.json_to_sheet(popupData, {
|
|
201
|
+
header: POPUP_COLUMNS.map((c) => c.header)
|
|
202
|
+
});
|
|
203
|
+
XLSX.utils.book_append_sheet(workbook, popupSheet, "Popup");
|
|
204
|
+
}
|
|
205
|
+
return workbook;
|
|
206
|
+
}
|
|
207
|
+
function generateXlsxBuffer(data, options = {}) {
|
|
208
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
209
|
+
return XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
|
|
210
|
+
}
|
|
211
|
+
function writeXlsxFile(data, outputPath, options = {}) {
|
|
212
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
213
|
+
XLSX.writeFile(workbook, outputPath);
|
|
214
|
+
}
|
|
215
|
+
function exportSiteDataToXlsx(siteData, outputPath) {
|
|
216
|
+
writeXlsxFile(siteData, outputPath);
|
|
217
|
+
}
|
|
218
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
219
|
+
0 && (module.exports = {
|
|
220
|
+
exportSiteDataToXlsx,
|
|
221
|
+
generateXlsxBuffer,
|
|
222
|
+
generateXlsxWorkbook,
|
|
223
|
+
writeXlsxFile
|
|
224
|
+
});
|
|
225
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/export/index.ts","../../src/export/xlsx.ts"],"sourcesContent":["/**\n * SheetSite Export Module\n *\n * Utilities for exporting site data to various formats.\n */\n\nexport {\n generateXlsxWorkbook,\n generateXlsxBuffer,\n writeXlsxFile,\n exportSiteDataToXlsx,\n type ExportOptions,\n} from './xlsx';\n","/**\n * XLSX Export for SheetSite\n *\n * Generates Excel workbooks from site data that can be imported into Google Sheets.\n * Each tab corresponds to a data section (business, hours, services, etc.)\n */\n\nimport * as XLSX from 'xlsx';\nimport type { SiteData, PartialSiteData } from '../data/types';\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ExportOptions {\n filename?: string;\n includeEmptyTabs?: boolean;\n}\n\n// =============================================================================\n// COLUMN MAPPINGS\n// =============================================================================\n\nconst BUSINESS_COLUMNS = [\n { key: 'name', header: 'name' },\n { key: 'tagline', header: 'tagline' },\n { key: 'description', header: 'description' },\n { key: 'aboutShort', header: 'aboutShort' },\n { key: 'phone', header: 'phone' },\n { key: 'email', header: 'email' },\n { key: 'addressLine1', header: 'addressLine1' },\n { key: 'addressLine2', header: 'addressLine2' },\n { key: 'city', header: 'city' },\n { key: 'state', header: 'state' },\n { key: 'zip', header: 'zip' },\n { key: 'timezone', header: 'timezone' },\n { key: 'googleMapsUrl', header: 'googleMapsUrl' },\n { key: 'heroImageUrl', header: 'heroImageUrl' },\n { key: 'logoUrl', header: 'logoUrl' },\n { key: 'primaryCtaText', header: 'primaryCtaText' },\n { key: 'primaryCtaUrl', header: 'primaryCtaUrl' },\n { key: 'socialYelp', header: 'socialYelp' },\n { key: 'socialInstagram', header: 'socialInstagram' },\n { key: 'socialFacebook', header: 'socialFacebook' },\n { key: 'priceRange', header: 'priceRange' },\n];\n\nconst HOURS_COLUMNS = [\n { key: 'day', header: 'day' },\n { key: 'open', header: 'open' },\n { key: 'close', header: 'close' },\n { key: 'closed', header: 'closed' },\n];\n\nconst SERVICES_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'name', header: 'name' },\n { key: 'title', header: 'title' },\n { key: 'description', header: 'description' },\n { key: 'priceNote', header: 'priceNote' },\n { key: 'price', header: 'price' },\n { key: 'duration', header: 'duration' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst TESTIMONIALS_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'quote', header: 'quote' },\n { key: 'name', header: 'name' },\n { key: 'context', header: 'context' },\n { key: 'rating', header: 'rating' },\n { key: 'source', header: 'source' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst FAQ_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'question', header: 'question' },\n { key: 'answer', header: 'answer' },\n { key: 'category', header: 'category' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst GALLERY_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'url', header: 'url' },\n { key: 'imageUrl', header: 'imageUrl' },\n { key: 'alt', header: 'alt' },\n { key: 'caption', header: 'caption' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst MENU_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'name', header: 'name' },\n { key: 'description', header: 'description' },\n { key: 'price', header: 'price' },\n { key: 'priceNote', header: 'priceNote' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'available', header: 'available' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst POPUP_COLUMNS = [\n { key: 'enabled', header: 'enabled' },\n { key: 'type', header: 'type' },\n { key: 'title', header: 'title' },\n { key: 'message', header: 'message' },\n { key: 'buttonText', header: 'buttonText' },\n { key: 'buttonUrl', header: 'buttonUrl' },\n { key: 'delay', header: 'delay' },\n];\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\nfunction objectToRow(obj: Record<string, unknown>, columns: { key: string; header: string }[]): Record<string, unknown> {\n const row: Record<string, unknown> = {};\n for (const col of columns) {\n const value = obj[col.key];\n // Convert booleans to strings for Google Sheets compatibility\n if (typeof value === 'boolean') {\n row[col.header] = value ? 'TRUE' : 'FALSE';\n } else if (value !== undefined && value !== null) {\n row[col.header] = value;\n } else {\n row[col.header] = '';\n }\n }\n return row;\n}\n\nfunction arrayToSheetData<T extends Record<string, unknown>>(\n items: T[],\n columns: { key: string; header: string }[]\n): Record<string, unknown>[] {\n return items.map(item => objectToRow(item as Record<string, unknown>, columns));\n}\n\n// =============================================================================\n// MAIN EXPORT FUNCTION\n// =============================================================================\n\n/**\n * Generate an XLSX workbook from site data.\n * Returns a Buffer that can be saved to a file.\n */\nexport function generateXlsxWorkbook(\n data: SiteData | PartialSiteData,\n options: ExportOptions = {}\n): XLSX.WorkBook {\n const workbook = XLSX.utils.book_new();\n\n // Business sheet (single row, transposed as key-value pairs for readability)\n if (data.business) {\n const businessData = [objectToRow(data.business as Record<string, unknown>, BUSINESS_COLUMNS)];\n const businessSheet = XLSX.utils.json_to_sheet(businessData, {\n header: BUSINESS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, businessSheet, 'Business');\n }\n\n // Hours sheet\n if (data.hours && (data.hours.length > 0 || options.includeEmptyTabs)) {\n const hoursData = arrayToSheetData(data.hours as Record<string, unknown>[], HOURS_COLUMNS);\n const hoursSheet = XLSX.utils.json_to_sheet(hoursData, {\n header: HOURS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, hoursSheet, 'Hours');\n }\n\n // Services sheet\n if (data.services && (data.services.length > 0 || options.includeEmptyTabs)) {\n const servicesData = arrayToSheetData(data.services as Record<string, unknown>[], SERVICES_COLUMNS);\n const servicesSheet = XLSX.utils.json_to_sheet(servicesData, {\n header: SERVICES_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, servicesSheet, 'Services');\n }\n\n // Testimonials sheet\n if (data.testimonials && (data.testimonials.length > 0 || options.includeEmptyTabs)) {\n const testimonialsData = arrayToSheetData(data.testimonials as Record<string, unknown>[], TESTIMONIALS_COLUMNS);\n const testimonialsSheet = XLSX.utils.json_to_sheet(testimonialsData, {\n header: TESTIMONIALS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, testimonialsSheet, 'Testimonials');\n }\n\n // FAQ sheet\n if (data.faq && (data.faq.length > 0 || options.includeEmptyTabs)) {\n const faqData = arrayToSheetData(data.faq as Record<string, unknown>[], FAQ_COLUMNS);\n const faqSheet = XLSX.utils.json_to_sheet(faqData, {\n header: FAQ_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, faqSheet, 'FAQ');\n }\n\n // Gallery sheet\n if (data.gallery && (data.gallery.length > 0 || options.includeEmptyTabs)) {\n const galleryData = arrayToSheetData(data.gallery as Record<string, unknown>[], GALLERY_COLUMNS);\n const gallerySheet = XLSX.utils.json_to_sheet(galleryData, {\n header: GALLERY_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, gallerySheet, 'Gallery');\n }\n\n // Menu sheet\n if (data.menu && (data.menu.length > 0 || options.includeEmptyTabs)) {\n const menuData = arrayToSheetData(data.menu as Record<string, unknown>[], MENU_COLUMNS);\n const menuSheet = XLSX.utils.json_to_sheet(menuData, {\n header: MENU_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, menuSheet, 'Menu');\n }\n\n // Popup config sheet\n if (data.popup) {\n const popupData = [objectToRow(data.popup as Record<string, unknown>, POPUP_COLUMNS)];\n const popupSheet = XLSX.utils.json_to_sheet(popupData, {\n header: POPUP_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, popupSheet, 'Popup');\n }\n\n return workbook;\n}\n\n/**\n * Generate an XLSX file buffer from site data.\n */\nexport function generateXlsxBuffer(\n data: SiteData | PartialSiteData,\n options: ExportOptions = {}\n): Buffer {\n const workbook = generateXlsxWorkbook(data, options);\n return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer;\n}\n\n/**\n * Write an XLSX file to disk.\n */\nexport function writeXlsxFile(\n data: SiteData | PartialSiteData,\n outputPath: string,\n options: ExportOptions = {}\n): void {\n const workbook = generateXlsxWorkbook(data, options);\n XLSX.writeFile(workbook, outputPath);\n}\n\n/**\n * Generate a Google Sheets-compatible XLSX from site-data.ts format.\n * This is the main function to use when exporting from a generated site.\n */\nexport function exportSiteDataToXlsx(\n siteData: {\n business: Record<string, unknown>;\n hours?: Array<Record<string, unknown>>;\n services?: Array<Record<string, unknown>>;\n testimonials?: Array<Record<string, unknown>>;\n faq?: Array<Record<string, unknown>>;\n gallery?: Array<Record<string, unknown>>;\n menu?: Array<Record<string, unknown>>;\n popup?: Record<string, unknown>;\n },\n outputPath: string\n): void {\n writeXlsxFile(siteData as PartialSiteData, outputPath);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// src/export/xlsx.ts
|
|
2
|
+
import * as XLSX from "xlsx";
|
|
3
|
+
var BUSINESS_COLUMNS = [
|
|
4
|
+
{ key: "name", header: "name" },
|
|
5
|
+
{ key: "tagline", header: "tagline" },
|
|
6
|
+
{ key: "description", header: "description" },
|
|
7
|
+
{ key: "aboutShort", header: "aboutShort" },
|
|
8
|
+
{ key: "phone", header: "phone" },
|
|
9
|
+
{ key: "email", header: "email" },
|
|
10
|
+
{ key: "addressLine1", header: "addressLine1" },
|
|
11
|
+
{ key: "addressLine2", header: "addressLine2" },
|
|
12
|
+
{ key: "city", header: "city" },
|
|
13
|
+
{ key: "state", header: "state" },
|
|
14
|
+
{ key: "zip", header: "zip" },
|
|
15
|
+
{ key: "timezone", header: "timezone" },
|
|
16
|
+
{ key: "googleMapsUrl", header: "googleMapsUrl" },
|
|
17
|
+
{ key: "heroImageUrl", header: "heroImageUrl" },
|
|
18
|
+
{ key: "logoUrl", header: "logoUrl" },
|
|
19
|
+
{ key: "primaryCtaText", header: "primaryCtaText" },
|
|
20
|
+
{ key: "primaryCtaUrl", header: "primaryCtaUrl" },
|
|
21
|
+
{ key: "socialYelp", header: "socialYelp" },
|
|
22
|
+
{ key: "socialInstagram", header: "socialInstagram" },
|
|
23
|
+
{ key: "socialFacebook", header: "socialFacebook" },
|
|
24
|
+
{ key: "priceRange", header: "priceRange" }
|
|
25
|
+
];
|
|
26
|
+
var HOURS_COLUMNS = [
|
|
27
|
+
{ key: "day", header: "day" },
|
|
28
|
+
{ key: "open", header: "open" },
|
|
29
|
+
{ key: "close", header: "close" },
|
|
30
|
+
{ key: "closed", header: "closed" }
|
|
31
|
+
];
|
|
32
|
+
var SERVICES_COLUMNS = [
|
|
33
|
+
{ key: "id", header: "id" },
|
|
34
|
+
{ key: "name", header: "name" },
|
|
35
|
+
{ key: "title", header: "title" },
|
|
36
|
+
{ key: "description", header: "description" },
|
|
37
|
+
{ key: "priceNote", header: "priceNote" },
|
|
38
|
+
{ key: "price", header: "price" },
|
|
39
|
+
{ key: "duration", header: "duration" },
|
|
40
|
+
{ key: "category", header: "category" },
|
|
41
|
+
{ key: "featured", header: "featured" },
|
|
42
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
43
|
+
];
|
|
44
|
+
var TESTIMONIALS_COLUMNS = [
|
|
45
|
+
{ key: "id", header: "id" },
|
|
46
|
+
{ key: "quote", header: "quote" },
|
|
47
|
+
{ key: "name", header: "name" },
|
|
48
|
+
{ key: "context", header: "context" },
|
|
49
|
+
{ key: "rating", header: "rating" },
|
|
50
|
+
{ key: "source", header: "source" },
|
|
51
|
+
{ key: "featured", header: "featured" },
|
|
52
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
53
|
+
];
|
|
54
|
+
var FAQ_COLUMNS = [
|
|
55
|
+
{ key: "id", header: "id" },
|
|
56
|
+
{ key: "question", header: "question" },
|
|
57
|
+
{ key: "answer", header: "answer" },
|
|
58
|
+
{ key: "category", header: "category" },
|
|
59
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
60
|
+
];
|
|
61
|
+
var GALLERY_COLUMNS = [
|
|
62
|
+
{ key: "id", header: "id" },
|
|
63
|
+
{ key: "url", header: "url" },
|
|
64
|
+
{ key: "imageUrl", header: "imageUrl" },
|
|
65
|
+
{ key: "alt", header: "alt" },
|
|
66
|
+
{ key: "caption", header: "caption" },
|
|
67
|
+
{ key: "category", header: "category" },
|
|
68
|
+
{ key: "featured", header: "featured" },
|
|
69
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
70
|
+
];
|
|
71
|
+
var MENU_COLUMNS = [
|
|
72
|
+
{ key: "id", header: "id" },
|
|
73
|
+
{ key: "name", header: "name" },
|
|
74
|
+
{ key: "description", header: "description" },
|
|
75
|
+
{ key: "price", header: "price" },
|
|
76
|
+
{ key: "priceNote", header: "priceNote" },
|
|
77
|
+
{ key: "category", header: "category" },
|
|
78
|
+
{ key: "featured", header: "featured" },
|
|
79
|
+
{ key: "available", header: "available" },
|
|
80
|
+
{ key: "sortOrder", header: "sortOrder" }
|
|
81
|
+
];
|
|
82
|
+
var POPUP_COLUMNS = [
|
|
83
|
+
{ key: "enabled", header: "enabled" },
|
|
84
|
+
{ key: "type", header: "type" },
|
|
85
|
+
{ key: "title", header: "title" },
|
|
86
|
+
{ key: "message", header: "message" },
|
|
87
|
+
{ key: "buttonText", header: "buttonText" },
|
|
88
|
+
{ key: "buttonUrl", header: "buttonUrl" },
|
|
89
|
+
{ key: "delay", header: "delay" }
|
|
90
|
+
];
|
|
91
|
+
function objectToRow(obj, columns) {
|
|
92
|
+
const row = {};
|
|
93
|
+
for (const col of columns) {
|
|
94
|
+
const value = obj[col.key];
|
|
95
|
+
if (typeof value === "boolean") {
|
|
96
|
+
row[col.header] = value ? "TRUE" : "FALSE";
|
|
97
|
+
} else if (value !== void 0 && value !== null) {
|
|
98
|
+
row[col.header] = value;
|
|
99
|
+
} else {
|
|
100
|
+
row[col.header] = "";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return row;
|
|
104
|
+
}
|
|
105
|
+
function arrayToSheetData(items, columns) {
|
|
106
|
+
return items.map((item) => objectToRow(item, columns));
|
|
107
|
+
}
|
|
108
|
+
function generateXlsxWorkbook(data, options = {}) {
|
|
109
|
+
const workbook = XLSX.utils.book_new();
|
|
110
|
+
if (data.business) {
|
|
111
|
+
const businessData = [objectToRow(data.business, BUSINESS_COLUMNS)];
|
|
112
|
+
const businessSheet = XLSX.utils.json_to_sheet(businessData, {
|
|
113
|
+
header: BUSINESS_COLUMNS.map((c) => c.header)
|
|
114
|
+
});
|
|
115
|
+
XLSX.utils.book_append_sheet(workbook, businessSheet, "Business");
|
|
116
|
+
}
|
|
117
|
+
if (data.hours && (data.hours.length > 0 || options.includeEmptyTabs)) {
|
|
118
|
+
const hoursData = arrayToSheetData(data.hours, HOURS_COLUMNS);
|
|
119
|
+
const hoursSheet = XLSX.utils.json_to_sheet(hoursData, {
|
|
120
|
+
header: HOURS_COLUMNS.map((c) => c.header)
|
|
121
|
+
});
|
|
122
|
+
XLSX.utils.book_append_sheet(workbook, hoursSheet, "Hours");
|
|
123
|
+
}
|
|
124
|
+
if (data.services && (data.services.length > 0 || options.includeEmptyTabs)) {
|
|
125
|
+
const servicesData = arrayToSheetData(data.services, SERVICES_COLUMNS);
|
|
126
|
+
const servicesSheet = XLSX.utils.json_to_sheet(servicesData, {
|
|
127
|
+
header: SERVICES_COLUMNS.map((c) => c.header)
|
|
128
|
+
});
|
|
129
|
+
XLSX.utils.book_append_sheet(workbook, servicesSheet, "Services");
|
|
130
|
+
}
|
|
131
|
+
if (data.testimonials && (data.testimonials.length > 0 || options.includeEmptyTabs)) {
|
|
132
|
+
const testimonialsData = arrayToSheetData(data.testimonials, TESTIMONIALS_COLUMNS);
|
|
133
|
+
const testimonialsSheet = XLSX.utils.json_to_sheet(testimonialsData, {
|
|
134
|
+
header: TESTIMONIALS_COLUMNS.map((c) => c.header)
|
|
135
|
+
});
|
|
136
|
+
XLSX.utils.book_append_sheet(workbook, testimonialsSheet, "Testimonials");
|
|
137
|
+
}
|
|
138
|
+
if (data.faq && (data.faq.length > 0 || options.includeEmptyTabs)) {
|
|
139
|
+
const faqData = arrayToSheetData(data.faq, FAQ_COLUMNS);
|
|
140
|
+
const faqSheet = XLSX.utils.json_to_sheet(faqData, {
|
|
141
|
+
header: FAQ_COLUMNS.map((c) => c.header)
|
|
142
|
+
});
|
|
143
|
+
XLSX.utils.book_append_sheet(workbook, faqSheet, "FAQ");
|
|
144
|
+
}
|
|
145
|
+
if (data.gallery && (data.gallery.length > 0 || options.includeEmptyTabs)) {
|
|
146
|
+
const galleryData = arrayToSheetData(data.gallery, GALLERY_COLUMNS);
|
|
147
|
+
const gallerySheet = XLSX.utils.json_to_sheet(galleryData, {
|
|
148
|
+
header: GALLERY_COLUMNS.map((c) => c.header)
|
|
149
|
+
});
|
|
150
|
+
XLSX.utils.book_append_sheet(workbook, gallerySheet, "Gallery");
|
|
151
|
+
}
|
|
152
|
+
if (data.menu && (data.menu.length > 0 || options.includeEmptyTabs)) {
|
|
153
|
+
const menuData = arrayToSheetData(data.menu, MENU_COLUMNS);
|
|
154
|
+
const menuSheet = XLSX.utils.json_to_sheet(menuData, {
|
|
155
|
+
header: MENU_COLUMNS.map((c) => c.header)
|
|
156
|
+
});
|
|
157
|
+
XLSX.utils.book_append_sheet(workbook, menuSheet, "Menu");
|
|
158
|
+
}
|
|
159
|
+
if (data.popup) {
|
|
160
|
+
const popupData = [objectToRow(data.popup, POPUP_COLUMNS)];
|
|
161
|
+
const popupSheet = XLSX.utils.json_to_sheet(popupData, {
|
|
162
|
+
header: POPUP_COLUMNS.map((c) => c.header)
|
|
163
|
+
});
|
|
164
|
+
XLSX.utils.book_append_sheet(workbook, popupSheet, "Popup");
|
|
165
|
+
}
|
|
166
|
+
return workbook;
|
|
167
|
+
}
|
|
168
|
+
function generateXlsxBuffer(data, options = {}) {
|
|
169
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
170
|
+
return XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
|
|
171
|
+
}
|
|
172
|
+
function writeXlsxFile(data, outputPath, options = {}) {
|
|
173
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
174
|
+
XLSX.writeFile(workbook, outputPath);
|
|
175
|
+
}
|
|
176
|
+
function exportSiteDataToXlsx(siteData, outputPath) {
|
|
177
|
+
writeXlsxFile(siteData, outputPath);
|
|
178
|
+
}
|
|
179
|
+
export {
|
|
180
|
+
exportSiteDataToXlsx,
|
|
181
|
+
generateXlsxBuffer,
|
|
182
|
+
generateXlsxWorkbook,
|
|
183
|
+
writeXlsxFile
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/export/xlsx.ts"],"sourcesContent":["/**\n * XLSX Export for SheetSite\n *\n * Generates Excel workbooks from site data that can be imported into Google Sheets.\n * Each tab corresponds to a data section (business, hours, services, etc.)\n */\n\nimport * as XLSX from 'xlsx';\nimport type { SiteData, PartialSiteData } from '../data/types';\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface ExportOptions {\n filename?: string;\n includeEmptyTabs?: boolean;\n}\n\n// =============================================================================\n// COLUMN MAPPINGS\n// =============================================================================\n\nconst BUSINESS_COLUMNS = [\n { key: 'name', header: 'name' },\n { key: 'tagline', header: 'tagline' },\n { key: 'description', header: 'description' },\n { key: 'aboutShort', header: 'aboutShort' },\n { key: 'phone', header: 'phone' },\n { key: 'email', header: 'email' },\n { key: 'addressLine1', header: 'addressLine1' },\n { key: 'addressLine2', header: 'addressLine2' },\n { key: 'city', header: 'city' },\n { key: 'state', header: 'state' },\n { key: 'zip', header: 'zip' },\n { key: 'timezone', header: 'timezone' },\n { key: 'googleMapsUrl', header: 'googleMapsUrl' },\n { key: 'heroImageUrl', header: 'heroImageUrl' },\n { key: 'logoUrl', header: 'logoUrl' },\n { key: 'primaryCtaText', header: 'primaryCtaText' },\n { key: 'primaryCtaUrl', header: 'primaryCtaUrl' },\n { key: 'socialYelp', header: 'socialYelp' },\n { key: 'socialInstagram', header: 'socialInstagram' },\n { key: 'socialFacebook', header: 'socialFacebook' },\n { key: 'priceRange', header: 'priceRange' },\n];\n\nconst HOURS_COLUMNS = [\n { key: 'day', header: 'day' },\n { key: 'open', header: 'open' },\n { key: 'close', header: 'close' },\n { key: 'closed', header: 'closed' },\n];\n\nconst SERVICES_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'name', header: 'name' },\n { key: 'title', header: 'title' },\n { key: 'description', header: 'description' },\n { key: 'priceNote', header: 'priceNote' },\n { key: 'price', header: 'price' },\n { key: 'duration', header: 'duration' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst TESTIMONIALS_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'quote', header: 'quote' },\n { key: 'name', header: 'name' },\n { key: 'context', header: 'context' },\n { key: 'rating', header: 'rating' },\n { key: 'source', header: 'source' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst FAQ_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'question', header: 'question' },\n { key: 'answer', header: 'answer' },\n { key: 'category', header: 'category' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst GALLERY_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'url', header: 'url' },\n { key: 'imageUrl', header: 'imageUrl' },\n { key: 'alt', header: 'alt' },\n { key: 'caption', header: 'caption' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst MENU_COLUMNS = [\n { key: 'id', header: 'id' },\n { key: 'name', header: 'name' },\n { key: 'description', header: 'description' },\n { key: 'price', header: 'price' },\n { key: 'priceNote', header: 'priceNote' },\n { key: 'category', header: 'category' },\n { key: 'featured', header: 'featured' },\n { key: 'available', header: 'available' },\n { key: 'sortOrder', header: 'sortOrder' },\n];\n\nconst POPUP_COLUMNS = [\n { key: 'enabled', header: 'enabled' },\n { key: 'type', header: 'type' },\n { key: 'title', header: 'title' },\n { key: 'message', header: 'message' },\n { key: 'buttonText', header: 'buttonText' },\n { key: 'buttonUrl', header: 'buttonUrl' },\n { key: 'delay', header: 'delay' },\n];\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\nfunction objectToRow(obj: Record<string, unknown>, columns: { key: string; header: string }[]): Record<string, unknown> {\n const row: Record<string, unknown> = {};\n for (const col of columns) {\n const value = obj[col.key];\n // Convert booleans to strings for Google Sheets compatibility\n if (typeof value === 'boolean') {\n row[col.header] = value ? 'TRUE' : 'FALSE';\n } else if (value !== undefined && value !== null) {\n row[col.header] = value;\n } else {\n row[col.header] = '';\n }\n }\n return row;\n}\n\nfunction arrayToSheetData<T extends Record<string, unknown>>(\n items: T[],\n columns: { key: string; header: string }[]\n): Record<string, unknown>[] {\n return items.map(item => objectToRow(item as Record<string, unknown>, columns));\n}\n\n// =============================================================================\n// MAIN EXPORT FUNCTION\n// =============================================================================\n\n/**\n * Generate an XLSX workbook from site data.\n * Returns a Buffer that can be saved to a file.\n */\nexport function generateXlsxWorkbook(\n data: SiteData | PartialSiteData,\n options: ExportOptions = {}\n): XLSX.WorkBook {\n const workbook = XLSX.utils.book_new();\n\n // Business sheet (single row, transposed as key-value pairs for readability)\n if (data.business) {\n const businessData = [objectToRow(data.business as Record<string, unknown>, BUSINESS_COLUMNS)];\n const businessSheet = XLSX.utils.json_to_sheet(businessData, {\n header: BUSINESS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, businessSheet, 'Business');\n }\n\n // Hours sheet\n if (data.hours && (data.hours.length > 0 || options.includeEmptyTabs)) {\n const hoursData = arrayToSheetData(data.hours as Record<string, unknown>[], HOURS_COLUMNS);\n const hoursSheet = XLSX.utils.json_to_sheet(hoursData, {\n header: HOURS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, hoursSheet, 'Hours');\n }\n\n // Services sheet\n if (data.services && (data.services.length > 0 || options.includeEmptyTabs)) {\n const servicesData = arrayToSheetData(data.services as Record<string, unknown>[], SERVICES_COLUMNS);\n const servicesSheet = XLSX.utils.json_to_sheet(servicesData, {\n header: SERVICES_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, servicesSheet, 'Services');\n }\n\n // Testimonials sheet\n if (data.testimonials && (data.testimonials.length > 0 || options.includeEmptyTabs)) {\n const testimonialsData = arrayToSheetData(data.testimonials as Record<string, unknown>[], TESTIMONIALS_COLUMNS);\n const testimonialsSheet = XLSX.utils.json_to_sheet(testimonialsData, {\n header: TESTIMONIALS_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, testimonialsSheet, 'Testimonials');\n }\n\n // FAQ sheet\n if (data.faq && (data.faq.length > 0 || options.includeEmptyTabs)) {\n const faqData = arrayToSheetData(data.faq as Record<string, unknown>[], FAQ_COLUMNS);\n const faqSheet = XLSX.utils.json_to_sheet(faqData, {\n header: FAQ_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, faqSheet, 'FAQ');\n }\n\n // Gallery sheet\n if (data.gallery && (data.gallery.length > 0 || options.includeEmptyTabs)) {\n const galleryData = arrayToSheetData(data.gallery as Record<string, unknown>[], GALLERY_COLUMNS);\n const gallerySheet = XLSX.utils.json_to_sheet(galleryData, {\n header: GALLERY_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, gallerySheet, 'Gallery');\n }\n\n // Menu sheet\n if (data.menu && (data.menu.length > 0 || options.includeEmptyTabs)) {\n const menuData = arrayToSheetData(data.menu as Record<string, unknown>[], MENU_COLUMNS);\n const menuSheet = XLSX.utils.json_to_sheet(menuData, {\n header: MENU_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, menuSheet, 'Menu');\n }\n\n // Popup config sheet\n if (data.popup) {\n const popupData = [objectToRow(data.popup as Record<string, unknown>, POPUP_COLUMNS)];\n const popupSheet = XLSX.utils.json_to_sheet(popupData, {\n header: POPUP_COLUMNS.map(c => c.header),\n });\n XLSX.utils.book_append_sheet(workbook, popupSheet, 'Popup');\n }\n\n return workbook;\n}\n\n/**\n * Generate an XLSX file buffer from site data.\n */\nexport function generateXlsxBuffer(\n data: SiteData | PartialSiteData,\n options: ExportOptions = {}\n): Buffer {\n const workbook = generateXlsxWorkbook(data, options);\n return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer;\n}\n\n/**\n * Write an XLSX file to disk.\n */\nexport function writeXlsxFile(\n data: SiteData | PartialSiteData,\n outputPath: string,\n options: ExportOptions = {}\n): void {\n const workbook = generateXlsxWorkbook(data, options);\n XLSX.writeFile(workbook, outputPath);\n}\n\n/**\n * Generate a Google Sheets-compatible XLSX from site-data.ts format.\n * This is the main function to use when exporting from a generated site.\n */\nexport function exportSiteDataToXlsx(\n siteData: {\n business: Record<string, unknown>;\n hours?: Array<Record<string, unknown>>;\n services?: Array<Record<string, unknown>>;\n testimonials?: Array<Record<string, unknown>>;\n faq?: Array<Record<string, unknown>>;\n gallery?: Array<Record<string, unknown>>;\n menu?: Array<Record<string, unknown>>;\n popup?: Record<string, unknown>;\n },\n outputPath: string\n): void {\n writeXlsxFile(siteData as PartialSiteData, outputPath);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws505/sheetsite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"types": "./dist/config/index.d.ts",
|
|
39
39
|
"import": "./dist/config/index.mjs",
|
|
40
40
|
"require": "./dist/config/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./export": {
|
|
43
|
+
"types": "./dist/export/index.d.ts",
|
|
44
|
+
"import": "./dist/export/index.mjs",
|
|
45
|
+
"require": "./dist/export/index.js"
|
|
41
46
|
}
|
|
42
47
|
},
|
|
43
48
|
"files": [
|
|
@@ -47,6 +52,7 @@
|
|
|
47
52
|
"src/data",
|
|
48
53
|
"src/theme",
|
|
49
54
|
"src/seo",
|
|
55
|
+
"src/export",
|
|
50
56
|
"README.md"
|
|
51
57
|
],
|
|
52
58
|
"scripts": {
|
|
@@ -79,6 +85,7 @@
|
|
|
79
85
|
"tailwindcss": ">=3.0.0"
|
|
80
86
|
},
|
|
81
87
|
"dependencies": {
|
|
88
|
+
"xlsx": "^0.18.5",
|
|
82
89
|
"zod": "^3.22.4"
|
|
83
90
|
},
|
|
84
91
|
"devDependencies": {
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLSX Export for SheetSite
|
|
3
|
+
*
|
|
4
|
+
* Generates Excel workbooks from site data that can be imported into Google Sheets.
|
|
5
|
+
* Each tab corresponds to a data section (business, hours, services, etc.)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as XLSX from 'xlsx';
|
|
9
|
+
import type { SiteData, PartialSiteData } from '../data/types';
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// TYPES
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export interface ExportOptions {
|
|
16
|
+
filename?: string;
|
|
17
|
+
includeEmptyTabs?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// COLUMN MAPPINGS
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
const BUSINESS_COLUMNS = [
|
|
25
|
+
{ key: 'name', header: 'name' },
|
|
26
|
+
{ key: 'tagline', header: 'tagline' },
|
|
27
|
+
{ key: 'description', header: 'description' },
|
|
28
|
+
{ key: 'aboutShort', header: 'aboutShort' },
|
|
29
|
+
{ key: 'phone', header: 'phone' },
|
|
30
|
+
{ key: 'email', header: 'email' },
|
|
31
|
+
{ key: 'addressLine1', header: 'addressLine1' },
|
|
32
|
+
{ key: 'addressLine2', header: 'addressLine2' },
|
|
33
|
+
{ key: 'city', header: 'city' },
|
|
34
|
+
{ key: 'state', header: 'state' },
|
|
35
|
+
{ key: 'zip', header: 'zip' },
|
|
36
|
+
{ key: 'timezone', header: 'timezone' },
|
|
37
|
+
{ key: 'googleMapsUrl', header: 'googleMapsUrl' },
|
|
38
|
+
{ key: 'heroImageUrl', header: 'heroImageUrl' },
|
|
39
|
+
{ key: 'logoUrl', header: 'logoUrl' },
|
|
40
|
+
{ key: 'primaryCtaText', header: 'primaryCtaText' },
|
|
41
|
+
{ key: 'primaryCtaUrl', header: 'primaryCtaUrl' },
|
|
42
|
+
{ key: 'socialYelp', header: 'socialYelp' },
|
|
43
|
+
{ key: 'socialInstagram', header: 'socialInstagram' },
|
|
44
|
+
{ key: 'socialFacebook', header: 'socialFacebook' },
|
|
45
|
+
{ key: 'priceRange', header: 'priceRange' },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const HOURS_COLUMNS = [
|
|
49
|
+
{ key: 'day', header: 'day' },
|
|
50
|
+
{ key: 'open', header: 'open' },
|
|
51
|
+
{ key: 'close', header: 'close' },
|
|
52
|
+
{ key: 'closed', header: 'closed' },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const SERVICES_COLUMNS = [
|
|
56
|
+
{ key: 'id', header: 'id' },
|
|
57
|
+
{ key: 'name', header: 'name' },
|
|
58
|
+
{ key: 'title', header: 'title' },
|
|
59
|
+
{ key: 'description', header: 'description' },
|
|
60
|
+
{ key: 'priceNote', header: 'priceNote' },
|
|
61
|
+
{ key: 'price', header: 'price' },
|
|
62
|
+
{ key: 'duration', header: 'duration' },
|
|
63
|
+
{ key: 'category', header: 'category' },
|
|
64
|
+
{ key: 'featured', header: 'featured' },
|
|
65
|
+
{ key: 'sortOrder', header: 'sortOrder' },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const TESTIMONIALS_COLUMNS = [
|
|
69
|
+
{ key: 'id', header: 'id' },
|
|
70
|
+
{ key: 'quote', header: 'quote' },
|
|
71
|
+
{ key: 'name', header: 'name' },
|
|
72
|
+
{ key: 'context', header: 'context' },
|
|
73
|
+
{ key: 'rating', header: 'rating' },
|
|
74
|
+
{ key: 'source', header: 'source' },
|
|
75
|
+
{ key: 'featured', header: 'featured' },
|
|
76
|
+
{ key: 'sortOrder', header: 'sortOrder' },
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const FAQ_COLUMNS = [
|
|
80
|
+
{ key: 'id', header: 'id' },
|
|
81
|
+
{ key: 'question', header: 'question' },
|
|
82
|
+
{ key: 'answer', header: 'answer' },
|
|
83
|
+
{ key: 'category', header: 'category' },
|
|
84
|
+
{ key: 'sortOrder', header: 'sortOrder' },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const GALLERY_COLUMNS = [
|
|
88
|
+
{ key: 'id', header: 'id' },
|
|
89
|
+
{ key: 'url', header: 'url' },
|
|
90
|
+
{ key: 'imageUrl', header: 'imageUrl' },
|
|
91
|
+
{ key: 'alt', header: 'alt' },
|
|
92
|
+
{ key: 'caption', header: 'caption' },
|
|
93
|
+
{ key: 'category', header: 'category' },
|
|
94
|
+
{ key: 'featured', header: 'featured' },
|
|
95
|
+
{ key: 'sortOrder', header: 'sortOrder' },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const MENU_COLUMNS = [
|
|
99
|
+
{ key: 'id', header: 'id' },
|
|
100
|
+
{ key: 'name', header: 'name' },
|
|
101
|
+
{ key: 'description', header: 'description' },
|
|
102
|
+
{ key: 'price', header: 'price' },
|
|
103
|
+
{ key: 'priceNote', header: 'priceNote' },
|
|
104
|
+
{ key: 'category', header: 'category' },
|
|
105
|
+
{ key: 'featured', header: 'featured' },
|
|
106
|
+
{ key: 'available', header: 'available' },
|
|
107
|
+
{ key: 'sortOrder', header: 'sortOrder' },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
const POPUP_COLUMNS = [
|
|
111
|
+
{ key: 'enabled', header: 'enabled' },
|
|
112
|
+
{ key: 'type', header: 'type' },
|
|
113
|
+
{ key: 'title', header: 'title' },
|
|
114
|
+
{ key: 'message', header: 'message' },
|
|
115
|
+
{ key: 'buttonText', header: 'buttonText' },
|
|
116
|
+
{ key: 'buttonUrl', header: 'buttonUrl' },
|
|
117
|
+
{ key: 'delay', header: 'delay' },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
// =============================================================================
|
|
121
|
+
// HELPERS
|
|
122
|
+
// =============================================================================
|
|
123
|
+
|
|
124
|
+
function objectToRow(obj: Record<string, unknown>, columns: { key: string; header: string }[]): Record<string, unknown> {
|
|
125
|
+
const row: Record<string, unknown> = {};
|
|
126
|
+
for (const col of columns) {
|
|
127
|
+
const value = obj[col.key];
|
|
128
|
+
// Convert booleans to strings for Google Sheets compatibility
|
|
129
|
+
if (typeof value === 'boolean') {
|
|
130
|
+
row[col.header] = value ? 'TRUE' : 'FALSE';
|
|
131
|
+
} else if (value !== undefined && value !== null) {
|
|
132
|
+
row[col.header] = value;
|
|
133
|
+
} else {
|
|
134
|
+
row[col.header] = '';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return row;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function arrayToSheetData<T extends Record<string, unknown>>(
|
|
141
|
+
items: T[],
|
|
142
|
+
columns: { key: string; header: string }[]
|
|
143
|
+
): Record<string, unknown>[] {
|
|
144
|
+
return items.map(item => objectToRow(item as Record<string, unknown>, columns));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// =============================================================================
|
|
148
|
+
// MAIN EXPORT FUNCTION
|
|
149
|
+
// =============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Generate an XLSX workbook from site data.
|
|
153
|
+
* Returns a Buffer that can be saved to a file.
|
|
154
|
+
*/
|
|
155
|
+
export function generateXlsxWorkbook(
|
|
156
|
+
data: SiteData | PartialSiteData,
|
|
157
|
+
options: ExportOptions = {}
|
|
158
|
+
): XLSX.WorkBook {
|
|
159
|
+
const workbook = XLSX.utils.book_new();
|
|
160
|
+
|
|
161
|
+
// Business sheet (single row, transposed as key-value pairs for readability)
|
|
162
|
+
if (data.business) {
|
|
163
|
+
const businessData = [objectToRow(data.business as Record<string, unknown>, BUSINESS_COLUMNS)];
|
|
164
|
+
const businessSheet = XLSX.utils.json_to_sheet(businessData, {
|
|
165
|
+
header: BUSINESS_COLUMNS.map(c => c.header),
|
|
166
|
+
});
|
|
167
|
+
XLSX.utils.book_append_sheet(workbook, businessSheet, 'Business');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Hours sheet
|
|
171
|
+
if (data.hours && (data.hours.length > 0 || options.includeEmptyTabs)) {
|
|
172
|
+
const hoursData = arrayToSheetData(data.hours as Record<string, unknown>[], HOURS_COLUMNS);
|
|
173
|
+
const hoursSheet = XLSX.utils.json_to_sheet(hoursData, {
|
|
174
|
+
header: HOURS_COLUMNS.map(c => c.header),
|
|
175
|
+
});
|
|
176
|
+
XLSX.utils.book_append_sheet(workbook, hoursSheet, 'Hours');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Services sheet
|
|
180
|
+
if (data.services && (data.services.length > 0 || options.includeEmptyTabs)) {
|
|
181
|
+
const servicesData = arrayToSheetData(data.services as Record<string, unknown>[], SERVICES_COLUMNS);
|
|
182
|
+
const servicesSheet = XLSX.utils.json_to_sheet(servicesData, {
|
|
183
|
+
header: SERVICES_COLUMNS.map(c => c.header),
|
|
184
|
+
});
|
|
185
|
+
XLSX.utils.book_append_sheet(workbook, servicesSheet, 'Services');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Testimonials sheet
|
|
189
|
+
if (data.testimonials && (data.testimonials.length > 0 || options.includeEmptyTabs)) {
|
|
190
|
+
const testimonialsData = arrayToSheetData(data.testimonials as Record<string, unknown>[], TESTIMONIALS_COLUMNS);
|
|
191
|
+
const testimonialsSheet = XLSX.utils.json_to_sheet(testimonialsData, {
|
|
192
|
+
header: TESTIMONIALS_COLUMNS.map(c => c.header),
|
|
193
|
+
});
|
|
194
|
+
XLSX.utils.book_append_sheet(workbook, testimonialsSheet, 'Testimonials');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// FAQ sheet
|
|
198
|
+
if (data.faq && (data.faq.length > 0 || options.includeEmptyTabs)) {
|
|
199
|
+
const faqData = arrayToSheetData(data.faq as Record<string, unknown>[], FAQ_COLUMNS);
|
|
200
|
+
const faqSheet = XLSX.utils.json_to_sheet(faqData, {
|
|
201
|
+
header: FAQ_COLUMNS.map(c => c.header),
|
|
202
|
+
});
|
|
203
|
+
XLSX.utils.book_append_sheet(workbook, faqSheet, 'FAQ');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Gallery sheet
|
|
207
|
+
if (data.gallery && (data.gallery.length > 0 || options.includeEmptyTabs)) {
|
|
208
|
+
const galleryData = arrayToSheetData(data.gallery as Record<string, unknown>[], GALLERY_COLUMNS);
|
|
209
|
+
const gallerySheet = XLSX.utils.json_to_sheet(galleryData, {
|
|
210
|
+
header: GALLERY_COLUMNS.map(c => c.header),
|
|
211
|
+
});
|
|
212
|
+
XLSX.utils.book_append_sheet(workbook, gallerySheet, 'Gallery');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Menu sheet
|
|
216
|
+
if (data.menu && (data.menu.length > 0 || options.includeEmptyTabs)) {
|
|
217
|
+
const menuData = arrayToSheetData(data.menu as Record<string, unknown>[], MENU_COLUMNS);
|
|
218
|
+
const menuSheet = XLSX.utils.json_to_sheet(menuData, {
|
|
219
|
+
header: MENU_COLUMNS.map(c => c.header),
|
|
220
|
+
});
|
|
221
|
+
XLSX.utils.book_append_sheet(workbook, menuSheet, 'Menu');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Popup config sheet
|
|
225
|
+
if (data.popup) {
|
|
226
|
+
const popupData = [objectToRow(data.popup as Record<string, unknown>, POPUP_COLUMNS)];
|
|
227
|
+
const popupSheet = XLSX.utils.json_to_sheet(popupData, {
|
|
228
|
+
header: POPUP_COLUMNS.map(c => c.header),
|
|
229
|
+
});
|
|
230
|
+
XLSX.utils.book_append_sheet(workbook, popupSheet, 'Popup');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return workbook;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generate an XLSX file buffer from site data.
|
|
238
|
+
*/
|
|
239
|
+
export function generateXlsxBuffer(
|
|
240
|
+
data: SiteData | PartialSiteData,
|
|
241
|
+
options: ExportOptions = {}
|
|
242
|
+
): Buffer {
|
|
243
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
244
|
+
return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Write an XLSX file to disk.
|
|
249
|
+
*/
|
|
250
|
+
export function writeXlsxFile(
|
|
251
|
+
data: SiteData | PartialSiteData,
|
|
252
|
+
outputPath: string,
|
|
253
|
+
options: ExportOptions = {}
|
|
254
|
+
): void {
|
|
255
|
+
const workbook = generateXlsxWorkbook(data, options);
|
|
256
|
+
XLSX.writeFile(workbook, outputPath);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Generate a Google Sheets-compatible XLSX from site-data.ts format.
|
|
261
|
+
* This is the main function to use when exporting from a generated site.
|
|
262
|
+
*/
|
|
263
|
+
export function exportSiteDataToXlsx(
|
|
264
|
+
siteData: {
|
|
265
|
+
business: Record<string, unknown>;
|
|
266
|
+
hours?: Array<Record<string, unknown>>;
|
|
267
|
+
services?: Array<Record<string, unknown>>;
|
|
268
|
+
testimonials?: Array<Record<string, unknown>>;
|
|
269
|
+
faq?: Array<Record<string, unknown>>;
|
|
270
|
+
gallery?: Array<Record<string, unknown>>;
|
|
271
|
+
menu?: Array<Record<string, unknown>>;
|
|
272
|
+
popup?: Record<string, unknown>;
|
|
273
|
+
},
|
|
274
|
+
outputPath: string
|
|
275
|
+
): void {
|
|
276
|
+
writeXlsxFile(siteData as PartialSiteData, outputPath);
|
|
277
|
+
}
|