@aws505/sheetsite 1.0.0

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 (57) hide show
  1. package/README.md +105 -0
  2. package/dist/components/index.js +1696 -0
  3. package/dist/components/index.js.map +1 -0
  4. package/dist/components/index.mjs +1630 -0
  5. package/dist/components/index.mjs.map +1 -0
  6. package/dist/config/index.js +1840 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/config/index.mjs +1793 -0
  9. package/dist/config/index.mjs.map +1 -0
  10. package/dist/data/index.js +1296 -0
  11. package/dist/data/index.js.map +1 -0
  12. package/dist/data/index.mjs +1220 -0
  13. package/dist/data/index.mjs.map +1 -0
  14. package/dist/index.js +5433 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/index.mjs +5285 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/seo/index.js +187 -0
  19. package/dist/seo/index.js.map +1 -0
  20. package/dist/seo/index.mjs +155 -0
  21. package/dist/seo/index.mjs.map +1 -0
  22. package/dist/theme/index.js +552 -0
  23. package/dist/theme/index.js.map +1 -0
  24. package/dist/theme/index.mjs +526 -0
  25. package/dist/theme/index.mjs.map +1 -0
  26. package/package.json +96 -0
  27. package/src/components/index.ts +41 -0
  28. package/src/components/layout/Footer.tsx +234 -0
  29. package/src/components/layout/Header.tsx +134 -0
  30. package/src/components/sections/FAQ.tsx +178 -0
  31. package/src/components/sections/Gallery.tsx +107 -0
  32. package/src/components/sections/Hero.tsx +202 -0
  33. package/src/components/sections/Hours.tsx +225 -0
  34. package/src/components/sections/Services.tsx +216 -0
  35. package/src/components/sections/Testimonials.tsx +184 -0
  36. package/src/components/ui/Button.tsx +158 -0
  37. package/src/components/ui/Card.tsx +162 -0
  38. package/src/components/ui/Icons.tsx +508 -0
  39. package/src/config/index.ts +207 -0
  40. package/src/config/presets/generic.ts +153 -0
  41. package/src/config/presets/home-kitchen.ts +154 -0
  42. package/src/config/presets/index.ts +708 -0
  43. package/src/config/presets/professional.ts +165 -0
  44. package/src/config/presets/repair.ts +160 -0
  45. package/src/config/presets/restaurant.ts +162 -0
  46. package/src/config/presets/salon.ts +178 -0
  47. package/src/config/presets/tailor.ts +159 -0
  48. package/src/config/types.ts +314 -0
  49. package/src/data/csv-parser.ts +154 -0
  50. package/src/data/defaults.ts +202 -0
  51. package/src/data/google-drive.ts +148 -0
  52. package/src/data/index.ts +535 -0
  53. package/src/data/sheets.ts +709 -0
  54. package/src/data/types.ts +379 -0
  55. package/src/seo/index.ts +272 -0
  56. package/src/theme/colors.ts +351 -0
  57. package/src/theme/index.ts +249 -0
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/seo/index.ts
21
+ var seo_exports = {};
22
+ __export(seo_exports, {
23
+ generateCommonMetaTags: () => generateCommonMetaTags,
24
+ generateJsonLdScript: () => generateJsonLdScript,
25
+ generateLocalBusinessJsonLd: () => generateLocalBusinessJsonLd,
26
+ generateMetadata: () => generateMetadata,
27
+ generateRobotsTxt: () => generateRobotsTxt,
28
+ generateSitemapUrls: () => generateSitemapUrls,
29
+ generateSitemapXml: () => generateSitemapXml,
30
+ getCanonicalUrl: () => getCanonicalUrl
31
+ });
32
+ module.exports = __toCommonJS(seo_exports);
33
+ function generateMetadata(business, pageTitle, pageDescription, options = {}) {
34
+ const { titleTemplate = "%s", baseUrl } = options;
35
+ const title = pageTitle ? titleTemplate.replace("%s", pageTitle) : business.name;
36
+ const description = pageDescription || business.description || business.aboutShort || "";
37
+ const locationString = [business.city, business.state].filter(Boolean).join(", ");
38
+ return {
39
+ title,
40
+ description,
41
+ keywords: [
42
+ business.name,
43
+ locationString
44
+ // Add more keywords based on business type
45
+ ].filter(Boolean),
46
+ openGraph: {
47
+ title,
48
+ description,
49
+ type: "website",
50
+ ...baseUrl && { url: baseUrl },
51
+ ...business.ogImageUrl && { images: [{ url: business.ogImageUrl }] }
52
+ },
53
+ twitter: {
54
+ card: "summary_large_image",
55
+ title,
56
+ description,
57
+ ...business.ogImageUrl && { images: [business.ogImageUrl] }
58
+ }
59
+ };
60
+ }
61
+ function convertTo24Hour(time) {
62
+ const match = time.match(/(\d{1,2}):?(\d{2})?\s*(AM|PM)?/i);
63
+ if (!match) return time;
64
+ let hours = parseInt(match[1], 10);
65
+ const minutes = match[2] || "00";
66
+ const period = match[3]?.toUpperCase();
67
+ if (period === "PM" && hours !== 12) hours += 12;
68
+ if (period === "AM" && hours === 12) hours = 0;
69
+ return `${hours.toString().padStart(2, "0")}:${minutes}`;
70
+ }
71
+ function generateOpeningHoursSpec(hours) {
72
+ const dayMap = {
73
+ monday: "Monday",
74
+ tuesday: "Tuesday",
75
+ wednesday: "Wednesday",
76
+ thursday: "Thursday",
77
+ friday: "Friday",
78
+ saturday: "Saturday",
79
+ sunday: "Sunday"
80
+ };
81
+ return hours.filter((h) => !h.closed && h.open && h.close).map((h) => ({
82
+ "@type": "OpeningHoursSpecification",
83
+ dayOfWeek: dayMap[h.day],
84
+ opens: convertTo24Hour(h.open),
85
+ closes: convertTo24Hour(h.close)
86
+ }));
87
+ }
88
+ function generateLocalBusinessJsonLd(data, options = {}) {
89
+ const { business, hours, testimonials } = data;
90
+ const { type = "LocalBusiness", baseUrl } = options;
91
+ const address = {
92
+ "@type": "PostalAddress",
93
+ streetAddress: [business.addressLine1, business.addressLine2].filter(Boolean).join(", "),
94
+ addressLocality: business.city,
95
+ addressRegion: business.state,
96
+ postalCode: business.zip,
97
+ addressCountry: business.country || "US"
98
+ };
99
+ const jsonLd = {
100
+ "@context": "https://schema.org",
101
+ "@type": type,
102
+ name: business.name,
103
+ description: business.description || business.aboutShort,
104
+ ...baseUrl && { url: baseUrl },
105
+ ...business.phone && { telephone: business.phone },
106
+ ...business.email && { email: business.email },
107
+ ...business.logoUrl && { logo: business.logoUrl },
108
+ ...business.heroImageUrl && { image: business.heroImageUrl },
109
+ ...business.addressLine1 && { address },
110
+ ...business.priceRange && { priceRange: business.priceRange },
111
+ ...hours.length > 0 && { openingHoursSpecification: generateOpeningHoursSpec(hours) }
112
+ };
113
+ if (testimonials.length > 0) {
114
+ const ratings = testimonials.filter((t) => t.rating).map((t) => t.rating);
115
+ if (ratings.length > 0) {
116
+ const avgRating = ratings.reduce((a, b) => a + b, 0) / ratings.length;
117
+ jsonLd.aggregateRating = {
118
+ "@type": "AggregateRating",
119
+ ratingValue: avgRating.toFixed(1),
120
+ reviewCount: ratings.length,
121
+ bestRating: 5,
122
+ worstRating: 1
123
+ };
124
+ }
125
+ }
126
+ return jsonLd;
127
+ }
128
+ function generateJsonLdScript(jsonLd) {
129
+ return JSON.stringify(jsonLd, null, 2);
130
+ }
131
+ function generateSitemapUrls(baseUrl, pages = ["/", "/services", "/gallery", "/about", "/contact", "/faq", "/privacy"]) {
132
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
133
+ return pages.map((page) => ({
134
+ loc: `${baseUrl}${page}`,
135
+ lastmod: now,
136
+ changefreq: page === "/" ? "weekly" : "monthly",
137
+ priority: page === "/" ? 1 : 0.8
138
+ }));
139
+ }
140
+ function generateSitemapXml(urls) {
141
+ const urlEntries = urls.map(
142
+ (url) => `
143
+ <url>
144
+ <loc>${url.loc}</loc>
145
+ ${url.lastmod ? `<lastmod>${url.lastmod}</lastmod>` : ""}
146
+ ${url.changefreq ? `<changefreq>${url.changefreq}</changefreq>` : ""}
147
+ ${url.priority !== void 0 ? `<priority>${url.priority}</priority>` : ""}
148
+ </url>`
149
+ ).join("");
150
+ return `<?xml version="1.0" encoding="UTF-8"?>
151
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
152
+ ${urlEntries}
153
+ </urlset>`;
154
+ }
155
+ function generateRobotsTxt(baseUrl) {
156
+ return `User-agent: *
157
+ Allow: /
158
+ ${baseUrl ? `
159
+ Sitemap: ${baseUrl}/sitemap.xml` : ""}`;
160
+ }
161
+ function getCanonicalUrl(baseUrl, path) {
162
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
163
+ return `${baseUrl}${cleanPath}`;
164
+ }
165
+ function generateCommonMetaTags(business) {
166
+ const location = [business.city, business.state].filter(Boolean).join(", ");
167
+ return {
168
+ "geo.region": business.state ? `US-${business.state}` : void 0,
169
+ "geo.placename": business.city,
170
+ "og:locale": "en_US",
171
+ "og:site_name": business.name,
172
+ "twitter:card": "summary_large_image",
173
+ ...location && { "geo.position": location }
174
+ };
175
+ }
176
+ // Annotate the CommonJS export names for ESM import in node:
177
+ 0 && (module.exports = {
178
+ generateCommonMetaTags,
179
+ generateJsonLdScript,
180
+ generateLocalBusinessJsonLd,
181
+ generateMetadata,
182
+ generateRobotsTxt,
183
+ generateSitemapUrls,
184
+ generateSitemapXml,
185
+ getCanonicalUrl
186
+ });
187
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/seo/index.ts"],"sourcesContent":["/**\n * SheetSite SEO Module\n *\n * Utilities for generating SEO metadata, JSON-LD structured data,\n * sitemaps, and robots.txt.\n */\n\nimport type { BusinessInfo, HoursEntry, SiteData } from '../data/types';\n\n// =============================================================================\n// METADATA GENERATION\n// =============================================================================\n\nexport interface MetadataOptions {\n titleTemplate?: string;\n baseUrl?: string;\n}\n\n/**\n * Generate metadata for a page.\n */\nexport function generateMetadata(\n business: BusinessInfo,\n pageTitle?: string,\n pageDescription?: string,\n options: MetadataOptions = {}\n) {\n const { titleTemplate = '%s', baseUrl } = options;\n\n const title = pageTitle\n ? titleTemplate.replace('%s', pageTitle)\n : business.name;\n\n const description = pageDescription || business.description || business.aboutShort || '';\n\n const locationString = [business.city, business.state].filter(Boolean).join(', ');\n\n return {\n title,\n description,\n keywords: [\n business.name,\n locationString,\n // Add more keywords based on business type\n ].filter(Boolean),\n openGraph: {\n title,\n description,\n type: 'website',\n ...(baseUrl && { url: baseUrl }),\n ...(business.ogImageUrl && { images: [{ url: business.ogImageUrl }] }),\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n ...(business.ogImageUrl && { images: [business.ogImageUrl] }),\n },\n };\n}\n\n// =============================================================================\n// JSON-LD STRUCTURED DATA\n// =============================================================================\n\nexport type JsonLdType =\n | 'LocalBusiness'\n | 'Restaurant'\n | 'Store'\n | 'ProfessionalService'\n | 'HealthAndBeautyBusiness'\n | 'FoodEstablishment'\n | 'HomeAndConstructionBusiness'\n | 'AutomotiveBusiness';\n\n/**\n * Convert 12-hour time to 24-hour format for JSON-LD.\n */\nfunction convertTo24Hour(time: string): string {\n const match = time.match(/(\\d{1,2}):?(\\d{2})?\\s*(AM|PM)?/i);\n if (!match) return time;\n\n let hours = parseInt(match[1], 10);\n const minutes = match[2] || '00';\n const period = match[3]?.toUpperCase();\n\n if (period === 'PM' && hours !== 12) hours += 12;\n if (period === 'AM' && hours === 12) hours = 0;\n\n return `${hours.toString().padStart(2, '0')}:${minutes}`;\n}\n\n/**\n * Generate opening hours specification for JSON-LD.\n */\nfunction generateOpeningHoursSpec(hours: HoursEntry[]) {\n const dayMap: Record<string, string> = {\n monday: 'Monday',\n tuesday: 'Tuesday',\n wednesday: 'Wednesday',\n thursday: 'Thursday',\n friday: 'Friday',\n saturday: 'Saturday',\n sunday: 'Sunday',\n };\n\n return hours\n .filter((h) => !h.closed && h.open && h.close)\n .map((h) => ({\n '@type': 'OpeningHoursSpecification',\n dayOfWeek: dayMap[h.day],\n opens: convertTo24Hour(h.open!),\n closes: convertTo24Hour(h.close!),\n }));\n}\n\n/**\n * Generate JSON-LD structured data for a local business.\n */\nexport function generateLocalBusinessJsonLd(\n data: SiteData,\n options: {\n type?: JsonLdType;\n baseUrl?: string;\n } = {}\n): object {\n const { business, hours, testimonials } = data;\n const { type = 'LocalBusiness', baseUrl } = options;\n\n const address = {\n '@type': 'PostalAddress',\n streetAddress: [business.addressLine1, business.addressLine2].filter(Boolean).join(', '),\n addressLocality: business.city,\n addressRegion: business.state,\n postalCode: business.zip,\n addressCountry: business.country || 'US',\n };\n\n const jsonLd: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': type,\n name: business.name,\n description: business.description || business.aboutShort,\n ...(baseUrl && { url: baseUrl }),\n ...(business.phone && { telephone: business.phone }),\n ...(business.email && { email: business.email }),\n ...(business.logoUrl && { logo: business.logoUrl }),\n ...(business.heroImageUrl && { image: business.heroImageUrl }),\n ...(business.addressLine1 && { address }),\n ...(business.priceRange && { priceRange: business.priceRange }),\n ...(hours.length > 0 && { openingHoursSpecification: generateOpeningHoursSpec(hours) }),\n };\n\n // Add aggregate rating if testimonials exist\n if (testimonials.length > 0) {\n const ratings = testimonials.filter((t) => t.rating).map((t) => t.rating!);\n if (ratings.length > 0) {\n const avgRating = ratings.reduce((a, b) => a + b, 0) / ratings.length;\n jsonLd.aggregateRating = {\n '@type': 'AggregateRating',\n ratingValue: avgRating.toFixed(1),\n reviewCount: ratings.length,\n bestRating: 5,\n worstRating: 1,\n };\n }\n }\n\n return jsonLd;\n}\n\n/**\n * Generate the script tag content for JSON-LD.\n */\nexport function generateJsonLdScript(jsonLd: object): string {\n return JSON.stringify(jsonLd, null, 2);\n}\n\n// =============================================================================\n// SITEMAP GENERATION\n// =============================================================================\n\nexport interface SitemapUrl {\n loc: string;\n lastmod?: string;\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\n priority?: number;\n}\n\n/**\n * Generate sitemap URLs for common pages.\n */\nexport function generateSitemapUrls(\n baseUrl: string,\n pages: string[] = ['/', '/services', '/gallery', '/about', '/contact', '/faq', '/privacy']\n): SitemapUrl[] {\n const now = new Date().toISOString().split('T')[0];\n\n return pages.map((page) => ({\n loc: `${baseUrl}${page}`,\n lastmod: now,\n changefreq: page === '/' ? 'weekly' : 'monthly',\n priority: page === '/' ? 1.0 : 0.8,\n }));\n}\n\n/**\n * Generate XML sitemap content.\n */\nexport function generateSitemapXml(urls: SitemapUrl[]): string {\n const urlEntries = urls\n .map(\n (url) => `\n <url>\n <loc>${url.loc}</loc>\n ${url.lastmod ? `<lastmod>${url.lastmod}</lastmod>` : ''}\n ${url.changefreq ? `<changefreq>${url.changefreq}</changefreq>` : ''}\n ${url.priority !== undefined ? `<priority>${url.priority}</priority>` : ''}\n </url>`\n )\n .join('');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urlEntries}\n</urlset>`;\n}\n\n// =============================================================================\n// ROBOTS.TXT GENERATION\n// =============================================================================\n\n/**\n * Generate robots.txt content.\n */\nexport function generateRobotsTxt(baseUrl?: string): string {\n return `User-agent: *\nAllow: /\n${baseUrl ? `\\nSitemap: ${baseUrl}/sitemap.xml` : ''}`;\n}\n\n// =============================================================================\n// CANONICAL URL HELPERS\n// =============================================================================\n\n/**\n * Generate canonical URL.\n */\nexport function getCanonicalUrl(baseUrl: string, path: string): string {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `${baseUrl}${cleanPath}`;\n}\n\n// =============================================================================\n// META TAGS HELPERS\n// =============================================================================\n\n/**\n * Generate common meta tags as an object.\n */\nexport function generateCommonMetaTags(business: BusinessInfo) {\n const location = [business.city, business.state].filter(Boolean).join(', ');\n\n return {\n 'geo.region': business.state ? `US-${business.state}` : undefined,\n 'geo.placename': business.city,\n 'og:locale': 'en_US',\n 'og:site_name': business.name,\n 'twitter:card': 'summary_large_image',\n ...(location && { 'geo.position': location }),\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,155 @@
1
+ // src/seo/index.ts
2
+ function generateMetadata(business, pageTitle, pageDescription, options = {}) {
3
+ const { titleTemplate = "%s", baseUrl } = options;
4
+ const title = pageTitle ? titleTemplate.replace("%s", pageTitle) : business.name;
5
+ const description = pageDescription || business.description || business.aboutShort || "";
6
+ const locationString = [business.city, business.state].filter(Boolean).join(", ");
7
+ return {
8
+ title,
9
+ description,
10
+ keywords: [
11
+ business.name,
12
+ locationString
13
+ // Add more keywords based on business type
14
+ ].filter(Boolean),
15
+ openGraph: {
16
+ title,
17
+ description,
18
+ type: "website",
19
+ ...baseUrl && { url: baseUrl },
20
+ ...business.ogImageUrl && { images: [{ url: business.ogImageUrl }] }
21
+ },
22
+ twitter: {
23
+ card: "summary_large_image",
24
+ title,
25
+ description,
26
+ ...business.ogImageUrl && { images: [business.ogImageUrl] }
27
+ }
28
+ };
29
+ }
30
+ function convertTo24Hour(time) {
31
+ const match = time.match(/(\d{1,2}):?(\d{2})?\s*(AM|PM)?/i);
32
+ if (!match) return time;
33
+ let hours = parseInt(match[1], 10);
34
+ const minutes = match[2] || "00";
35
+ const period = match[3]?.toUpperCase();
36
+ if (period === "PM" && hours !== 12) hours += 12;
37
+ if (period === "AM" && hours === 12) hours = 0;
38
+ return `${hours.toString().padStart(2, "0")}:${minutes}`;
39
+ }
40
+ function generateOpeningHoursSpec(hours) {
41
+ const dayMap = {
42
+ monday: "Monday",
43
+ tuesday: "Tuesday",
44
+ wednesday: "Wednesday",
45
+ thursday: "Thursday",
46
+ friday: "Friday",
47
+ saturday: "Saturday",
48
+ sunday: "Sunday"
49
+ };
50
+ return hours.filter((h) => !h.closed && h.open && h.close).map((h) => ({
51
+ "@type": "OpeningHoursSpecification",
52
+ dayOfWeek: dayMap[h.day],
53
+ opens: convertTo24Hour(h.open),
54
+ closes: convertTo24Hour(h.close)
55
+ }));
56
+ }
57
+ function generateLocalBusinessJsonLd(data, options = {}) {
58
+ const { business, hours, testimonials } = data;
59
+ const { type = "LocalBusiness", baseUrl } = options;
60
+ const address = {
61
+ "@type": "PostalAddress",
62
+ streetAddress: [business.addressLine1, business.addressLine2].filter(Boolean).join(", "),
63
+ addressLocality: business.city,
64
+ addressRegion: business.state,
65
+ postalCode: business.zip,
66
+ addressCountry: business.country || "US"
67
+ };
68
+ const jsonLd = {
69
+ "@context": "https://schema.org",
70
+ "@type": type,
71
+ name: business.name,
72
+ description: business.description || business.aboutShort,
73
+ ...baseUrl && { url: baseUrl },
74
+ ...business.phone && { telephone: business.phone },
75
+ ...business.email && { email: business.email },
76
+ ...business.logoUrl && { logo: business.logoUrl },
77
+ ...business.heroImageUrl && { image: business.heroImageUrl },
78
+ ...business.addressLine1 && { address },
79
+ ...business.priceRange && { priceRange: business.priceRange },
80
+ ...hours.length > 0 && { openingHoursSpecification: generateOpeningHoursSpec(hours) }
81
+ };
82
+ if (testimonials.length > 0) {
83
+ const ratings = testimonials.filter((t) => t.rating).map((t) => t.rating);
84
+ if (ratings.length > 0) {
85
+ const avgRating = ratings.reduce((a, b) => a + b, 0) / ratings.length;
86
+ jsonLd.aggregateRating = {
87
+ "@type": "AggregateRating",
88
+ ratingValue: avgRating.toFixed(1),
89
+ reviewCount: ratings.length,
90
+ bestRating: 5,
91
+ worstRating: 1
92
+ };
93
+ }
94
+ }
95
+ return jsonLd;
96
+ }
97
+ function generateJsonLdScript(jsonLd) {
98
+ return JSON.stringify(jsonLd, null, 2);
99
+ }
100
+ function generateSitemapUrls(baseUrl, pages = ["/", "/services", "/gallery", "/about", "/contact", "/faq", "/privacy"]) {
101
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
102
+ return pages.map((page) => ({
103
+ loc: `${baseUrl}${page}`,
104
+ lastmod: now,
105
+ changefreq: page === "/" ? "weekly" : "monthly",
106
+ priority: page === "/" ? 1 : 0.8
107
+ }));
108
+ }
109
+ function generateSitemapXml(urls) {
110
+ const urlEntries = urls.map(
111
+ (url) => `
112
+ <url>
113
+ <loc>${url.loc}</loc>
114
+ ${url.lastmod ? `<lastmod>${url.lastmod}</lastmod>` : ""}
115
+ ${url.changefreq ? `<changefreq>${url.changefreq}</changefreq>` : ""}
116
+ ${url.priority !== void 0 ? `<priority>${url.priority}</priority>` : ""}
117
+ </url>`
118
+ ).join("");
119
+ return `<?xml version="1.0" encoding="UTF-8"?>
120
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
121
+ ${urlEntries}
122
+ </urlset>`;
123
+ }
124
+ function generateRobotsTxt(baseUrl) {
125
+ return `User-agent: *
126
+ Allow: /
127
+ ${baseUrl ? `
128
+ Sitemap: ${baseUrl}/sitemap.xml` : ""}`;
129
+ }
130
+ function getCanonicalUrl(baseUrl, path) {
131
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
132
+ return `${baseUrl}${cleanPath}`;
133
+ }
134
+ function generateCommonMetaTags(business) {
135
+ const location = [business.city, business.state].filter(Boolean).join(", ");
136
+ return {
137
+ "geo.region": business.state ? `US-${business.state}` : void 0,
138
+ "geo.placename": business.city,
139
+ "og:locale": "en_US",
140
+ "og:site_name": business.name,
141
+ "twitter:card": "summary_large_image",
142
+ ...location && { "geo.position": location }
143
+ };
144
+ }
145
+ export {
146
+ generateCommonMetaTags,
147
+ generateJsonLdScript,
148
+ generateLocalBusinessJsonLd,
149
+ generateMetadata,
150
+ generateRobotsTxt,
151
+ generateSitemapUrls,
152
+ generateSitemapXml,
153
+ getCanonicalUrl
154
+ };
155
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/seo/index.ts"],"sourcesContent":["/**\n * SheetSite SEO Module\n *\n * Utilities for generating SEO metadata, JSON-LD structured data,\n * sitemaps, and robots.txt.\n */\n\nimport type { BusinessInfo, HoursEntry, SiteData } from '../data/types';\n\n// =============================================================================\n// METADATA GENERATION\n// =============================================================================\n\nexport interface MetadataOptions {\n titleTemplate?: string;\n baseUrl?: string;\n}\n\n/**\n * Generate metadata for a page.\n */\nexport function generateMetadata(\n business: BusinessInfo,\n pageTitle?: string,\n pageDescription?: string,\n options: MetadataOptions = {}\n) {\n const { titleTemplate = '%s', baseUrl } = options;\n\n const title = pageTitle\n ? titleTemplate.replace('%s', pageTitle)\n : business.name;\n\n const description = pageDescription || business.description || business.aboutShort || '';\n\n const locationString = [business.city, business.state].filter(Boolean).join(', ');\n\n return {\n title,\n description,\n keywords: [\n business.name,\n locationString,\n // Add more keywords based on business type\n ].filter(Boolean),\n openGraph: {\n title,\n description,\n type: 'website',\n ...(baseUrl && { url: baseUrl }),\n ...(business.ogImageUrl && { images: [{ url: business.ogImageUrl }] }),\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n ...(business.ogImageUrl && { images: [business.ogImageUrl] }),\n },\n };\n}\n\n// =============================================================================\n// JSON-LD STRUCTURED DATA\n// =============================================================================\n\nexport type JsonLdType =\n | 'LocalBusiness'\n | 'Restaurant'\n | 'Store'\n | 'ProfessionalService'\n | 'HealthAndBeautyBusiness'\n | 'FoodEstablishment'\n | 'HomeAndConstructionBusiness'\n | 'AutomotiveBusiness';\n\n/**\n * Convert 12-hour time to 24-hour format for JSON-LD.\n */\nfunction convertTo24Hour(time: string): string {\n const match = time.match(/(\\d{1,2}):?(\\d{2})?\\s*(AM|PM)?/i);\n if (!match) return time;\n\n let hours = parseInt(match[1], 10);\n const minutes = match[2] || '00';\n const period = match[3]?.toUpperCase();\n\n if (period === 'PM' && hours !== 12) hours += 12;\n if (period === 'AM' && hours === 12) hours = 0;\n\n return `${hours.toString().padStart(2, '0')}:${minutes}`;\n}\n\n/**\n * Generate opening hours specification for JSON-LD.\n */\nfunction generateOpeningHoursSpec(hours: HoursEntry[]) {\n const dayMap: Record<string, string> = {\n monday: 'Monday',\n tuesday: 'Tuesday',\n wednesday: 'Wednesday',\n thursday: 'Thursday',\n friday: 'Friday',\n saturday: 'Saturday',\n sunday: 'Sunday',\n };\n\n return hours\n .filter((h) => !h.closed && h.open && h.close)\n .map((h) => ({\n '@type': 'OpeningHoursSpecification',\n dayOfWeek: dayMap[h.day],\n opens: convertTo24Hour(h.open!),\n closes: convertTo24Hour(h.close!),\n }));\n}\n\n/**\n * Generate JSON-LD structured data for a local business.\n */\nexport function generateLocalBusinessJsonLd(\n data: SiteData,\n options: {\n type?: JsonLdType;\n baseUrl?: string;\n } = {}\n): object {\n const { business, hours, testimonials } = data;\n const { type = 'LocalBusiness', baseUrl } = options;\n\n const address = {\n '@type': 'PostalAddress',\n streetAddress: [business.addressLine1, business.addressLine2].filter(Boolean).join(', '),\n addressLocality: business.city,\n addressRegion: business.state,\n postalCode: business.zip,\n addressCountry: business.country || 'US',\n };\n\n const jsonLd: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': type,\n name: business.name,\n description: business.description || business.aboutShort,\n ...(baseUrl && { url: baseUrl }),\n ...(business.phone && { telephone: business.phone }),\n ...(business.email && { email: business.email }),\n ...(business.logoUrl && { logo: business.logoUrl }),\n ...(business.heroImageUrl && { image: business.heroImageUrl }),\n ...(business.addressLine1 && { address }),\n ...(business.priceRange && { priceRange: business.priceRange }),\n ...(hours.length > 0 && { openingHoursSpecification: generateOpeningHoursSpec(hours) }),\n };\n\n // Add aggregate rating if testimonials exist\n if (testimonials.length > 0) {\n const ratings = testimonials.filter((t) => t.rating).map((t) => t.rating!);\n if (ratings.length > 0) {\n const avgRating = ratings.reduce((a, b) => a + b, 0) / ratings.length;\n jsonLd.aggregateRating = {\n '@type': 'AggregateRating',\n ratingValue: avgRating.toFixed(1),\n reviewCount: ratings.length,\n bestRating: 5,\n worstRating: 1,\n };\n }\n }\n\n return jsonLd;\n}\n\n/**\n * Generate the script tag content for JSON-LD.\n */\nexport function generateJsonLdScript(jsonLd: object): string {\n return JSON.stringify(jsonLd, null, 2);\n}\n\n// =============================================================================\n// SITEMAP GENERATION\n// =============================================================================\n\nexport interface SitemapUrl {\n loc: string;\n lastmod?: string;\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\n priority?: number;\n}\n\n/**\n * Generate sitemap URLs for common pages.\n */\nexport function generateSitemapUrls(\n baseUrl: string,\n pages: string[] = ['/', '/services', '/gallery', '/about', '/contact', '/faq', '/privacy']\n): SitemapUrl[] {\n const now = new Date().toISOString().split('T')[0];\n\n return pages.map((page) => ({\n loc: `${baseUrl}${page}`,\n lastmod: now,\n changefreq: page === '/' ? 'weekly' : 'monthly',\n priority: page === '/' ? 1.0 : 0.8,\n }));\n}\n\n/**\n * Generate XML sitemap content.\n */\nexport function generateSitemapXml(urls: SitemapUrl[]): string {\n const urlEntries = urls\n .map(\n (url) => `\n <url>\n <loc>${url.loc}</loc>\n ${url.lastmod ? `<lastmod>${url.lastmod}</lastmod>` : ''}\n ${url.changefreq ? `<changefreq>${url.changefreq}</changefreq>` : ''}\n ${url.priority !== undefined ? `<priority>${url.priority}</priority>` : ''}\n </url>`\n )\n .join('');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urlEntries}\n</urlset>`;\n}\n\n// =============================================================================\n// ROBOTS.TXT GENERATION\n// =============================================================================\n\n/**\n * Generate robots.txt content.\n */\nexport function generateRobotsTxt(baseUrl?: string): string {\n return `User-agent: *\nAllow: /\n${baseUrl ? `\\nSitemap: ${baseUrl}/sitemap.xml` : ''}`;\n}\n\n// =============================================================================\n// CANONICAL URL HELPERS\n// =============================================================================\n\n/**\n * Generate canonical URL.\n */\nexport function getCanonicalUrl(baseUrl: string, path: string): string {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return `${baseUrl}${cleanPath}`;\n}\n\n// =============================================================================\n// META TAGS HELPERS\n// =============================================================================\n\n/**\n * Generate common meta tags as an object.\n */\nexport function generateCommonMetaTags(business: BusinessInfo) {\n const location = [business.city, business.state].filter(Boolean).join(', ');\n\n return {\n 'geo.region': business.state ? `US-${business.state}` : undefined,\n 'geo.placename': business.city,\n 'og:locale': 'en_US',\n 'og:site_name': business.name,\n 'twitter:card': 'summary_large_image',\n ...(location && { 'geo.position': location }),\n };\n}\n"],"mappings":"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","names":[]}