feat(events): Add script to fetch and display Zoho calendar events
This commit introduces a new script that fetches and normalizes event data from Zoho Calendar using OAuth2. The script makes an API call to retrieve calendar events for the current month. The event data is then processed and formatted to be used by a Nunjucks template. Key Changes: - test(dummy-data): Created a dummy calendar and dummy events for development and testing. - feat(events): Added a new script to fetch event data from Zoho Calendar. - feat(oauth2): Implemented OAuth2 authentication for secure API access. - refactor(data): Normalized the fetched data payload for consistency. - feat(ui): Integrated the normalized data with a Nunjucks template to dynamically populate "event cards" on the community events page.
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
module.exports = function (eleventyConfig) {
|
||||
// ✅ Pass through static assets to the root of output
|
||||
eleventyConfig.addPassthroughCopy({ "src/images": "images" });
|
||||
eleventyConfig.addPassthroughCopy({ "src/styles": "styles" });
|
||||
eleventyConfig.addPassthroughCopy({ "src/scripts": "scripts" });
|
||||
|
||||
return {
|
||||
dir: {
|
||||
input: "src",
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
node_modules/
|
||||
_site/
|
||||
_site/*
|
||||
.env
|
||||
|
613
package-lock.json
generated
613
package-lock.json
generated
@@ -5,6 +5,10 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"curl": "^0.1.4",
|
||||
"dotenv": "^17.2.2",
|
||||
"ical.js": "^2.2.1",
|
||||
"node-fetch": "^2.7.0",
|
||||
"servor": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -262,6 +266,22 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
@@ -346,6 +366,45 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/aws4": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -395,6 +454,15 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@@ -432,6 +500,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -457,6 +531,18 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
@@ -474,11 +560,40 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/curl": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/curl/-/curl-0.1.4.tgz",
|
||||
"integrity": "sha512-pkdngpcm+UluREtOIqNpPLwLkb9JWth+v3zLn9Uc7Ca4ErP81zyS5OofqRq+zli1dSfuKHquI5eQYsVI6BOHjQ==",
|
||||
"dependencies": {
|
||||
"request": ">=0.1.0",
|
||||
"router": ">=0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -492,11 +607,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
@@ -581,6 +704,28 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.2.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
|
||||
"integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -688,6 +833,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
@@ -701,6 +852,27 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@@ -778,6 +950,50 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||
@@ -803,6 +1019,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@@ -856,6 +1081,29 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"deprecated": "this library is no longer supported",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
|
||||
@@ -916,6 +1164,27 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8",
|
||||
"npm": ">=1.3.7"
|
||||
}
|
||||
},
|
||||
"node_modules/ical.js": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-2.2.1.tgz",
|
||||
"integrity": "sha512-yK/UlPbEs316igb/tjRgbFA8ZV75rCsBJp/hWOatpyaPNlgw0dGDmU+FoicOcwX4xXkeXOkYiOmCqNPFpNPkQg==",
|
||||
"license": "MPL-2.0"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
@@ -1023,6 +1292,18 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iso-639-1": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-3.1.5.tgz",
|
||||
@@ -1033,6 +1314,12 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@@ -1046,6 +1333,45 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
|
||||
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.4.0",
|
||||
"verror": "1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/junk": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz",
|
||||
@@ -1265,9 +1591,28 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-retrieve-globals": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-retrieve-globals/-/node-retrieve-globals-6.0.1.tgz",
|
||||
@@ -1326,6 +1671,15 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -1350,12 +1704,27 @@
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
@@ -1439,6 +1808,27 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/lupomontero"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
@@ -1449,6 +1839,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -1485,6 +1884,101 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.2",
|
||||
"har-validator": "~5.1.3",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.5.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/request/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/request/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/section-matter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
@@ -1584,6 +2078,31 @@
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"bin": {
|
||||
"sshpk-conv": "bin/sshpk-conv",
|
||||
"sshpk-sign": "bin/sshpk-sign",
|
||||
"sshpk-verify": "bin/sshpk-verify"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-11.0.0.tgz",
|
||||
@@ -1657,6 +2176,43 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
@@ -1674,6 +2230,15 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/urlpattern-polyfill": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz",
|
||||
@@ -1681,6 +2246,46 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
|
@@ -3,6 +3,10 @@
|
||||
"start": "servor --reload"
|
||||
},
|
||||
"dependencies": {
|
||||
"curl": "^0.1.4",
|
||||
"dotenv": "^17.2.2",
|
||||
"ical.js": "^2.2.1",
|
||||
"node-fetch": "^2.7.0",
|
||||
"servor": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
120
src/_data/communityEvents.js
Normal file
120
src/_data/communityEvents.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @fileoverview This data file contains all community events for Bloom Valley Nursery Demo.
|
||||
*/
|
||||
|
||||
const communityEvents = [
|
||||
// BVN Workshops
|
||||
{
|
||||
id: "WS-01",
|
||||
title: "Gardening and Landscaping 101",
|
||||
description: "Join Bethany Bloom as she shares tips for creating a themed decor throughout the home that you will be delighted to share with your holiday guests.",
|
||||
dayOfWeek: "Tuesday",
|
||||
time: "6:30 pm",
|
||||
month: "September",
|
||||
year: 2025,
|
||||
type: "bvn",
|
||||
category: "workshop"
|
||||
},
|
||||
{
|
||||
id: "WS-02",
|
||||
title: "Gardening and Landscaping 101",
|
||||
description: "Join Bethany Bloom as she shares tips for creating a themed decor throughout the home that you will be delighted to share with your holiday guests.",
|
||||
dayOfWeek: "Tuesday",
|
||||
time: "6:30 pm",
|
||||
month: "October",
|
||||
year: 2025,
|
||||
type: "bvn",
|
||||
category: "workshop"
|
||||
},
|
||||
{
|
||||
id: "WS-03",
|
||||
title: "Gardening and Landscaping 101",
|
||||
description: "Join Bethany Bloom as she shares tips for creating a themed decor throughout the home that you will be delighted to share with your holiday guests.",
|
||||
dayOfWeek: "Tuesday",
|
||||
time: "6:30 pm",
|
||||
month: "November",
|
||||
year: 2025,
|
||||
type: "bvn",
|
||||
category: "workshop"
|
||||
},
|
||||
{
|
||||
id: "WS-04",
|
||||
title: "Deck the Halls",
|
||||
description: "Join Nathaniel and Vincent as they guide us through picking and tending the perfect live Christmas tree for your Christmas decoration focal-point.",
|
||||
dayOfWeek: "Saturday",
|
||||
time: "10:00 am",
|
||||
month: "November",
|
||||
year: 2025,
|
||||
type: "bvn",
|
||||
category: "workshop"
|
||||
},
|
||||
|
||||
// BVN Events
|
||||
{
|
||||
id: "CE-01",
|
||||
title: "Christmas Eve and Christmas Day Closure",
|
||||
description: "Bloom Valley Nursery will close on Christmas Eve at 8:00 pm. The nursery will remain closed until January 2nd while we make improvements to the nursery grounds and store. Happy Holidays!",
|
||||
dayOfWeek: "Wednesday",
|
||||
time: "8:00 pm",
|
||||
month: "December",
|
||||
year: 2025,
|
||||
type: "bvn",
|
||||
category: "event"
|
||||
},
|
||||
|
||||
// Community-Wide Events
|
||||
{
|
||||
id: "CE-02",
|
||||
title: "Tree Lighting Ceremony",
|
||||
description: "Help us kick off the Holiday festivities by joining the Bloom family, along with the Bloom Valley Chamber of Commerce, the Bloom Valley Garden Club, and other local organizations, in front of City Hall. Includes performances, workshops, and the annual Chili Cook-off.",
|
||||
dayOfWeek: "Thursday",
|
||||
time: "6:00 pm",
|
||||
month: "December",
|
||||
year: 2025,
|
||||
type: "community",
|
||||
location: "City Hall",
|
||||
category: "event"
|
||||
},
|
||||
{
|
||||
id: "CE-03",
|
||||
title: "Halloween Spooktacular",
|
||||
description: "Join the Bloom Valley community for an evening of family-friendly Halloween fun, including costume contests, pumpkin carving, and trick-or-treating around the nursery.",
|
||||
dayOfWeek: "Friday",
|
||||
time: "5:00 pm",
|
||||
month: "October",
|
||||
year: 2025,
|
||||
type: "community",
|
||||
location: "Bloom Valley Nursery",
|
||||
category: "event"
|
||||
},
|
||||
{
|
||||
id: "CE-04",
|
||||
title: "Thanksgiving Harvest Festival",
|
||||
description: "Celebrate the season with a community Thanksgiving festival. Enjoy local food vendors, a farmers' market, and workshops on holiday décor and seasonal planting.",
|
||||
dayOfWeek: "Thursday",
|
||||
time: "11:00 am",
|
||||
month: "November",
|
||||
year: 2025,
|
||||
type: "community",
|
||||
location: "Bloom Valley Community Park",
|
||||
category: "event"
|
||||
},
|
||||
|
||||
// Cleanup Event
|
||||
{
|
||||
id: "CU-01",
|
||||
title: "Community Garden Workday",
|
||||
description: "Help keep our community garden thriving! Volunteers will help with planting, weeding, and general maintenance. All tools provided.",
|
||||
dayOfWeek: "Saturday",
|
||||
time: "9:00 am",
|
||||
month: "October",
|
||||
year: 2025,
|
||||
type: "community",
|
||||
location: "Bloom Valley Community Garden",
|
||||
category: "cleanup"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
communityEvents
|
||||
};
|
@@ -39,7 +39,7 @@ const credits = [
|
||||
license_url: "https://unsplash.com/license"
|
||||
},
|
||||
{
|
||||
file_name: "maple-tree.jpg",
|
||||
file_name: "japanese-maple-tree.jpg",
|
||||
category: "trees",
|
||||
creator: "yamasa-n",
|
||||
creator_url: "https://unsplash.com/@heppoko_yama",
|
||||
@@ -139,7 +139,7 @@ const credits = [
|
||||
license_url: "https://unsplash.com/license"
|
||||
},
|
||||
{
|
||||
file_name: "fire-pit.jpg",
|
||||
file_name: "firepit.jpg",
|
||||
category: "patio",
|
||||
creator: "Tim Gouw",
|
||||
creator_url: "https://unsplash.com/@punttim",
|
||||
|
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* @fileoverview This file contains the product data for
|
||||
* all items in the store demo. The data is structured
|
||||
* to be the single source of truth for all
|
||||
* product-related information, including IDs, pricing,
|
||||
* and descriptions.
|
||||
*/
|
||||
|
||||
/* **Unit Template**
|
||||
{
|
||||
id: "",
|
||||
image_file: "",
|
||||
sku: "",
|
||||
product_name: "",
|
||||
category: "",
|
||||
price: ,
|
||||
alt_text: "",
|
||||
short_description: "",
|
||||
long_description: "",
|
||||
ratings: {
|
||||
count: ,
|
||||
score:
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const inventory = [
|
||||
{
|
||||
"accessories": [
|
||||
{
|
||||
"id": "01001",
|
||||
"image_file": "bird-house.jpg",
|
||||
"sku": "ACC-01001",
|
||||
"product_name": "Bird House - Pink",
|
||||
"category": "accessories",
|
||||
"price": 12.99,
|
||||
"alt_text": "A pink handcrafted wooden bird house",
|
||||
"short_description": "A handcrafted wooden bird house in pink.",
|
||||
"long_description": "A beautifully handcrafted wooden bird house in a gentle pink finish, built by a local carpenter from the Bloom Valley community. Designed with a classic aesthetic and durable, weather-resistant materials, this bird house provides a cozy and safe home for small songbirds. It's a charming accent for any garden or tree and a perfect way to invite nature into your outdoor space.",
|
||||
"ratings": {
|
||||
"count": 38,
|
||||
"score": 4.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "01002",
|
||||
"image_file": "garden-pots.jpg",
|
||||
"sku": "ACC-01002",
|
||||
"product_name": "Gardening Pots - Terra-cotta",
|
||||
"category": "accessories",
|
||||
"price": 9.99,
|
||||
"alt_text": "A set of three traditional terra-cotta gardening pots of varying sizes.",
|
||||
"short_description": "A classic set of three terra-cotta gardening pots.",
|
||||
"long_description": "Crafted from porous, natural clay, this set of terra-cotta pots is a timeless choice for any gardener. The material's ability to absorb excess water helps prevent root rot, making it an ideal home for a wide variety of plants. The set includes three different sizes, perfect for potting new seedlings or housing larger plants.",
|
||||
"ratings": {
|
||||
"count": 150,
|
||||
"score": 4.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "01003",
|
||||
"image_file": "watering-can.jpg",
|
||||
"sku": "ACC-01003",
|
||||
"product_name": "Watering Can",
|
||||
"category": "accessories",
|
||||
"price": 19.99,
|
||||
"alt_text": "A modern, metal watering can with a long spout.",
|
||||
"short_description": "A stylish and functional metal watering can.",
|
||||
"long_description": "This sleek and durable metal watering can features a long, narrow spout for precise watering, perfect for both indoor and outdoor plants. Its comfortable handle provides a balanced grip, and the sturdy construction ensures it will be a reliable tool for years to come.",
|
||||
"ratings": {
|
||||
"count": 85,
|
||||
"score": 4.8
|
||||
}
|
||||
}
|
||||
],
|
||||
"indoor": [
|
||||
{
|
||||
"id": "02001",
|
||||
"image_file": "spider-plant.jpg",
|
||||
"sku": "IND-02001",
|
||||
"product_name": "Spider Plant",
|
||||
"category": "indoor",
|
||||
"price": 14.99,
|
||||
"alt_text": "A lush spider plant with long, variegated green and white leaves.",
|
||||
"short_description": "An easy-to-care-for spider plant, perfect for beginners.",
|
||||
"long_description": "The spider plant (Chlorophytum comosum) is one of the most popular and easiest houseplants to grow. Known for its distinctive arching, variegated leaves, it thrives in a variety of conditions and is a great choice for hanging baskets. It's also a highly effective air purifier.",
|
||||
"ratings": {
|
||||
"count": 210,
|
||||
"score": 4.9
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "02002",
|
||||
"image_file": "string-of-pearls.jpg",
|
||||
"sku": "IND-02002",
|
||||
"product_name": "String-of-Pearls Plant",
|
||||
"category": "indoor",
|
||||
"price": 16.99,
|
||||
"alt_text": "A cascading string-of-pearls plant with small, spherical green leaves.",
|
||||
"short_description": "A unique succulent with cascading, pearl-like foliage.",
|
||||
"long_description": "The string-of-pearls (Senecio rowleyanus) is a striking succulent known for its long, trailing stems dotted with small, bead-like leaves. It's a fantastic plant for a sunny spot on a shelf or in a hanging planter, creating a beautiful, flowing green display. Like most succulents, it requires minimal watering.",
|
||||
"ratings": {
|
||||
"count": 95,
|
||||
"score": 4.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "02003",
|
||||
"image_file": "zebra-plant.jpg",
|
||||
"sku": "IND-02003",
|
||||
"product_name": "Zebra Plant",
|
||||
"category": "indoor",
|
||||
"price": 18.99,
|
||||
"alt_text": "A vibrant zebra plant with deep green leaves and prominent white veins.",
|
||||
"short_description": "A striking houseplant with bold, striped leaves.",
|
||||
"long_description": "The zebra plant (Aphelandra squarrosa) is named for its eye-catching foliage—deep green leaves with stark, contrasting white or yellow veins. This plant adds a tropical feel to any room and, with the right care, can also produce brilliant yellow flower bracts. It prefers bright, indirect light and high humidity.",
|
||||
"ratings": {
|
||||
"count": 72,
|
||||
"score": 4.4
|
||||
}
|
||||
}
|
||||
],
|
||||
"landscape": [
|
||||
{
|
||||
"id": "03001",
|
||||
"image_file": "bark-mulch.jpg",
|
||||
"sku": "LND-03001",
|
||||
"product_name": "Pine Bark Mulch (25lb bag)",
|
||||
"category": "landscaping",
|
||||
"price": 8.99,
|
||||
"alt_text": "A 25lb bag of natural pine bark mulch.",
|
||||
"short_description": "A 25lb bag of premium pine bark mulch for landscaping.",
|
||||
"long_description": "Pine bark mulch is a top choice for gardeners and landscapers. It helps suppress weeds, retain soil moisture, and moderate soil temperature, which is essential for healthy plant growth. As it decomposes, it adds valuable organic matter to the soil. The rich, dark color also provides an attractive finish to garden beds and pathways.",
|
||||
"ratings": {
|
||||
"count": 305,
|
||||
"score": 4.8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "03002",
|
||||
"image_file": "pea-gravel.jpg",
|
||||
"sku": "LND-03002",
|
||||
"product_name": "Pea Gravel (25lb bag)",
|
||||
"category": "landscaping",
|
||||
"price": 11.99,
|
||||
"alt_text": "A 25lb bag of smooth, rounded pea gravel.",
|
||||
"short_description": "A 25lb bag of decorative pea gravel for landscaping.",
|
||||
"long_description": "Pea gravel consists of small, smooth, rounded stones, typically in a mix of neutral colors. It's an excellent choice for creating walkways, patios, and decorative ground cover. The smooth texture is comfortable to walk on, and it provides effective drainage and a clean, finished look to any landscape design.",
|
||||
"ratings": {
|
||||
"count": 185,
|
||||
"score": 4.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "03003",
|
||||
"image_file": "stone-pavers.jpg",
|
||||
"sku": "LND-03003",
|
||||
"product_name": "Stone Pavers (single count)",
|
||||
"category": "landscaping",
|
||||
"price": 4.50,
|
||||
"alt_text": "A single rectangular gray stone paver.",
|
||||
"short_description": "A durable, single stone paver for creating paths and patios.",
|
||||
"long_description": "Our stone pavers are made from high-quality concrete and are perfect for building durable, long-lasting patios, walkways, and driveways. The classic rectangular shape and neutral gray color make them versatile for a wide range of landscape designs. Sold individually, allowing you to purchase the exact quantity you need for your project.",
|
||||
"ratings": {
|
||||
"count": 255,
|
||||
"score": 4.6
|
||||
}
|
||||
}
|
||||
],
|
||||
"patio": [
|
||||
{
|
||||
"id": "04001",
|
||||
"image_file": "firepit.jpg",
|
||||
"sku": "PAT-04001",
|
||||
"product_name": "Metal Fire-pit",
|
||||
"category": "patio",
|
||||
"price": 129.99,
|
||||
"alt_text": "A round, black metal fire-pit with a mesh lid.",
|
||||
"short_description": "A sturdy and stylish metal fire-pit for outdoor gatherings.",
|
||||
"long_description": "Create a warm and inviting atmosphere on your patio with this durable metal fire-pit. Constructed from heat-resistant steel, it features a classic bowl design and includes a mesh lid for safety. Perfect for cool evenings, it's a great centerpiece for entertaining and enjoying the outdoors with friends and family.",
|
||||
"ratings": {
|
||||
"count": 55,
|
||||
"score": 4.9
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "04002",
|
||||
"image_file": "string-lights.jpg",
|
||||
"sku": "PAT-04002",
|
||||
"product_name": "LED String Lights",
|
||||
"category": "patio",
|
||||
"price": 24.99,
|
||||
"alt_text": "A string of warm white LED lights hanging outdoors.",
|
||||
"short_description": "A set of energy-efficient LED string lights for ambient lighting.",
|
||||
"long_description": "Add a touch of magic to your outdoor space with these energy-efficient LED string lights. The warm white glow creates a soft, inviting ambiance, perfect for backyard dinners, parties, or simply relaxing on your patio. The bulbs are shatterproof and weather-resistant, making them ideal for year-round outdoor use.",
|
||||
"ratings": {
|
||||
"count": 175,
|
||||
"score": 4.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "04003",
|
||||
"image_file": "parlor-palm.jpg",
|
||||
"sku": "PAT-04003",
|
||||
"product_name": "Parlor Palm",
|
||||
"category": "patio",
|
||||
"price": 22.99,
|
||||
"alt_text": "A small, potted parlor palm with feathery green fronds.",
|
||||
"short_description": "An elegant and low-maintenance palm for patios and decks.",
|
||||
"long_description": "The parlor palm (Chamaedorea elegans) is a classic choice for adding a tropical flair to a covered patio, deck, or indoor space. Known for its graceful, feathery fronds, it's a resilient and slow-growing plant that thrives in low light conditions. It's a great way to bring a lush, green aesthetic to your outdoor living area.",
|
||||
"ratings": {
|
||||
"count": 88,
|
||||
"score": 4.6
|
||||
}
|
||||
}
|
||||
],
|
||||
"shrubs": [
|
||||
{
|
||||
"id": "05001",
|
||||
"image_file": "hydrangea.jpg",
|
||||
"sku": "SHR-05001",
|
||||
"product_name": "Hydrangeas",
|
||||
"category": "shrubs",
|
||||
"price": 25.99,
|
||||
"alt_text": "A vibrant cluster of pink and purple hydrangea flowers.",
|
||||
"short_description": "A beautiful hydrangea bush (Hydrangea macrophylla), known for its large, colorful blooms.",
|
||||
"long_description": "Hydrangeas are a quintessential garden shrub, celebrated for their large, showy flower heads. Depending on the soil's pH, the blooms can change color, ranging from deep blues and purples to bright pinks. They are perfect for adding a splash of color to borders, foundations, or as a standalone focal point in the garden.",
|
||||
"ratings": {
|
||||
"count": 190,
|
||||
"score": 4.9
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "05002",
|
||||
"image_file": "crepe-myrtle.jpg",
|
||||
"sku": "SHR-05002",
|
||||
"product_name": "Crepe Myrtle",
|
||||
"category": "shrubs",
|
||||
"price": 28.99,
|
||||
"alt_text": "A crepe myrtle bush with clusters of bright pink flowers.",
|
||||
"short_description": "A small crepe myrtle tree, prized for its long-lasting summer blooms.",
|
||||
"long_description": "The crepe myrtle (Lagerstroemia) is a beloved small tree or shrub, famous for its crepe-paper-like blossoms that last all summer long. Its smooth, multi-hued bark and vibrant fall foliage also add year-round interest to the landscape. This fast-growing plant thrives in sunny locations and requires minimal care.",
|
||||
"ratings": {
|
||||
"count": 115,
|
||||
"score": 4.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "05003",
|
||||
"image_file": "holly.jpg",
|
||||
"sku": "SHR-05003",
|
||||
"product_name": "Holly",
|
||||
"category": "shrubs",
|
||||
"price": 21.99,
|
||||
"alt_text": "A holly bush with deep green leaves and bright red berries.",
|
||||
"short_description": "A classic evergreen holly bush with traditional red berries.",
|
||||
"long_description": "Holly (Ilex) is a traditional favorite for its glossy, evergreen leaves and bright red berries that provide winter interest. It's an excellent choice for creating hedges, screens, or as a structural plant in the garden. This hardy shrub is a staple for year-round beauty and can attract birds with its berries.",
|
||||
"ratings": {
|
||||
"count": 130,
|
||||
"score": 4.8
|
||||
}
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"id": "06001",
|
||||
"image_file": "gardening-tool.jpg",
|
||||
"sku": "TLS-06001",
|
||||
"product_name": "Gardening Tool Set",
|
||||
"category": "tools",
|
||||
"price": 34.99,
|
||||
"alt_text": "A set of three gardening tools: a trowel, a cultivator, and a transplanter.",
|
||||
"short_description": "A three-piece set of essential gardening tools.",
|
||||
"long_description": "This durable gardening tool set includes a trowel for planting, a cultivator for aerating soil, and a transplanter for moving seedlings. Each tool features a comfortable handle and a sturdy, rust-resistant head. It's the perfect starter set for anyone looking to get their hands dirty in the garden.",
|
||||
"ratings": {
|
||||
"count": 200,
|
||||
"score": 4.7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "06002",
|
||||
"image_file": "gardening-hose.jpg",
|
||||
"sku": "TLS-06002",
|
||||
"product_name": "Gardening Hose",
|
||||
"category": "tools",
|
||||
"price": 49.99,
|
||||
"alt_text": "A coiled green gardening hose.",
|
||||
"short_description": "A kink-resistant and durable gardening hose.",
|
||||
"long_description": "Our premium gardening hose is made from a high-quality, flexible material that resists kinking and tangling. It's lightweight yet incredibly durable, making watering your garden a breeze. The hose comes with solid brass couplings for a secure, leak-free connection to your spigot.",
|
||||
"ratings": {
|
||||
"count": 110,
|
||||
"score": 4.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "06003",
|
||||
"image_file": "wheelbarrow.jpg",
|
||||
"sku": "TLS-06003",
|
||||
"product_name": "Wheelbarrow",
|
||||
"category": "tools",
|
||||
"price": 99.99,
|
||||
"alt_text": "A sturdy single-wheeled wheelbarrow with a metal basin.",
|
||||
"short_description": "A heavy-duty wheelbarrow for all your hauling needs.",
|
||||
"long_description": "This robust wheelbarrow is an essential tool for any serious gardener or landscaper. It features a deep, heavy-duty steel basin and a strong single wheel for easy maneuverability, even on uneven terrain. Perfect for transporting soil, mulch, stones, and other heavy materials around your yard.",
|
||||
"ratings": {
|
||||
"count": 65,
|
||||
"score": 4.9
|
||||
}
|
||||
}
|
||||
],
|
||||
"trees": [
|
||||
{
|
||||
"id": "07001",
|
||||
"image_file": "apple-tree.jpg",
|
||||
"sku": "TRS-07001",
|
||||
"product_name": "Apple Tree (Honeycrisp)",
|
||||
"category": "trees",
|
||||
"price": 45.99,
|
||||
"alt_text": "A young Honeycrisp apple tree in a pot.",
|
||||
"short_description": "A young Honeycrisp apple tree (Malus domestica), known for its crisp and sweet fruit.",
|
||||
"long_description": "The Honeycrisp apple tree is a popular choice for home orchards, celebrated for its exceptionally crisp texture and deliciously sweet flavor. This semi-dwarf tree is relatively easy to care for and produces a bountiful harvest of apples in the late summer and early fall. Plant it in a sunny spot to ensure a great yield.",
|
||||
"ratings": {
|
||||
"count": 140,
|
||||
"score": 4.8
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "07002",
|
||||
"image_file": "silver-birch.jpg",
|
||||
"sku": "TRS-07002",
|
||||
"product_name": "Silver Birch",
|
||||
"category": "trees",
|
||||
"price": 39.99,
|
||||
"alt_text": "A young silver birch tree with smooth, white bark.",
|
||||
"short_description": "A graceful silver birch tree, known for its iconic white bark.",
|
||||
"long_description": "The silver birch (Betula pendula) is a stunning ornamental tree prized for its peeling, silvery-white bark and elegant, pendulous branches. It provides year-round visual interest and is an excellent choice for a focal point in your yard. This hardy tree is fast-growing and adds a touch of classic beauty to any landscape.",
|
||||
"ratings": {
|
||||
"count": 95,
|
||||
"score": 4.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "07003",
|
||||
"image_file": "japanese-maple-tree.jpg",
|
||||
"sku": "TRS-07003",
|
||||
"product_name": "Japanese Maple",
|
||||
"category": "trees",
|
||||
"price": 55.99,
|
||||
"alt_text": "A young Japanese Maple tree with lobed red and orange leaves.",
|
||||
"short_description": "A Japanese Maple tree, famous for its vibrant fall colors.",
|
||||
"long_description": "Japanese Maple trees (Acer palmatum) are a native of landscapes in northeastern Asia, cherished for their beautiful, lobed leaves and spectacular fall foliage. This young tree will grow into a stunning accent to your garden, providing a vibrant backdrop and a brilliant display of reds, oranges, and yellows in the autumn. It's a robust and long-living addition to any property.",
|
||||
"ratings": {
|
||||
"count": 220,
|
||||
"score": 4.9
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
42
src/_data/promotions.js
Normal file
42
src/_data/promotions.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @fileoverview This data file contains all of the promotion information
|
||||
* for the Featured Perks promotional carousel.
|
||||
*/
|
||||
|
||||
/* **Promo Template**
|
||||
{
|
||||
"promotion": "",
|
||||
"display": "",
|
||||
"description": "",
|
||||
"start_date": "",
|
||||
"end_date": ""
|
||||
}
|
||||
*/
|
||||
|
||||
const promos = [
|
||||
{
|
||||
id: "50-disc",
|
||||
title: "50% Off Discount!",
|
||||
description: "New subscribers this month will receive a coupon for 50% off their first purchase! Terms and conditions apply.",
|
||||
endDate: "2026-01-31",
|
||||
ctaText: "Subscribe Now"
|
||||
},
|
||||
{
|
||||
id: "free-install",
|
||||
title: "Free Installation on Select Purchases!",
|
||||
description: "Receive free installation on purchased items for your garden or patio. Applies to select inventory only.",
|
||||
endDate: "2025-09-30",
|
||||
ctaText: "Learn More"
|
||||
},
|
||||
{
|
||||
id: "B2G1-promo",
|
||||
title: "Buy 2, Get 1 Free!",
|
||||
description: "Buy any two trees, shrubs, or indoor/patio plants and get one for free! See associate for more details.",
|
||||
endDate: "2025-10-31",
|
||||
ctaText: "Shop Now"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
promos
|
||||
};
|
37
src/_data/testimonials.js
Normal file
37
src/_data/testimonials.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @fileoverview This data file contains all testimonials for the Testimonials Feature.
|
||||
*/
|
||||
|
||||
/* **Unit Template**
|
||||
{
|
||||
"id": "",
|
||||
"clientName": "",
|
||||
"testimonial": "",
|
||||
"dateObtained": ""
|
||||
}
|
||||
*/
|
||||
|
||||
const testimonials = [
|
||||
{
|
||||
"id": "T01",
|
||||
"clientName": "Caroline N.",
|
||||
"testimonial": "My order arrived the very next day. The speed of delivery and high-quality products provided by Bloom Valley Nursery helped my project go smoothly from start to finish!",
|
||||
"dateObtained": "2024-11-23"
|
||||
},
|
||||
{
|
||||
"id": "T02",
|
||||
"clientName": "Morris F.",
|
||||
"testimonial": "Thanks to the Gardening 101 Workshop with Bethany and Bloom Valley Nursery's amazing line of products, I won 'Best Azaleas' at the 2024 Royal Horticultural Society's American Cup!",
|
||||
"dateObtained": "2024-12-03"
|
||||
},
|
||||
{
|
||||
"id": "T03",
|
||||
"clientName": "Gladis B.",
|
||||
"testimonial": "Bloom Valley Nursery's community garden inspired me take charge of my health. I was able to lose 50 pounds!",
|
||||
"dateObtained": "2025-03-22"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
testimonials
|
||||
};
|
154
src/_data/zohoCalendarEvents.js
Normal file
154
src/_data/zohoCalendarEvents.js
Normal file
@@ -0,0 +1,154 @@
|
||||
require('dotenv').config();
|
||||
const fetch = require('node-fetch');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const calendarUid = process.env.ZOHO_CALENDAR_UID;
|
||||
let accessToken = process.env.ZOHO_ACCESS_TOKEN;
|
||||
const refreshToken = process.env.ZOHO_REFRESH_TOKEN;
|
||||
const clientId = process.env.ZOHO_CLIENT_ID;
|
||||
const clientSecret = process.env.ZOHO_CLIENT_SECRET;
|
||||
|
||||
// Dates for current month
|
||||
const currentDate = new Date();
|
||||
const currentYear = currentDate.getFullYear();
|
||||
const currentMonth = (currentDate.getMonth() + 1).toString().padStart(2, '0');
|
||||
const lastDay = new Date(currentYear, currentDate.getMonth() + 1, 0).getDate();
|
||||
const startDate = `${currentYear}${currentMonth}01`;
|
||||
const endDate = `${currentYear}${currentMonth}${lastDay}`;
|
||||
|
||||
const zohoApiUrl = `https://calendar.zoho.com/api/v1/calendars/${calendarUid}`;
|
||||
|
||||
async function refreshAccessToken() {
|
||||
const params = new URLSearchParams();
|
||||
params.append('grant_type', 'refresh_token');
|
||||
params.append('client_id', clientId);
|
||||
params.append('client_secret', clientSecret);
|
||||
params.append('refresh_token', refreshToken);
|
||||
|
||||
const response = await fetch('https://accounts.zoho.com/oauth/v2/token', {
|
||||
method: 'POST',
|
||||
body: params
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!data.access_token) {
|
||||
throw new Error('Failed to refresh access token: ' + JSON.stringify(data));
|
||||
}
|
||||
|
||||
const envPath = path.resolve(process.cwd(), '.env');
|
||||
const envContents = fs.readFileSync(envPath, 'utf-8')
|
||||
.split('\n')
|
||||
.map(line => line.startsWith('ZOHO_ACCESS_TOKEN=') ? `ZOHO_ACCESS_TOKEN=${data.access_token}` : line)
|
||||
.join('\n');
|
||||
fs.writeFileSync(envPath, envContents, 'utf-8');
|
||||
|
||||
accessToken = data.access_token;
|
||||
}
|
||||
|
||||
function parseZohoTimestamp(start, isAllDay) {
|
||||
if (isAllDay) {
|
||||
const year = parseInt(start.slice(0, 4));
|
||||
const month = parseInt(start.slice(4, 6)) - 1;
|
||||
const day = parseInt(start.slice(6, 8));
|
||||
return new Date(year, month, day);
|
||||
} else {
|
||||
const year = parseInt(start.slice(0, 4));
|
||||
const month = parseInt(start.slice(4, 6)) - 1;
|
||||
const day = parseInt(start.slice(6, 8));
|
||||
const hour = parseInt(start.slice(9, 11));
|
||||
const minute = parseInt(start.slice(11, 13));
|
||||
const second = parseInt(start.slice(13, 15));
|
||||
return new Date(year, month, day, hour, minute, second);
|
||||
}
|
||||
}
|
||||
|
||||
function formatEventDate(startDate, endDate, isAllDay) {
|
||||
const start = parseZohoTimestamp(startDate, isAllDay);
|
||||
if (isAllDay) {
|
||||
return start.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
} else {
|
||||
const end = parseZohoTimestamp(endDate, isAllDay);
|
||||
const optionsDate = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
||||
const optionsTime = { hour: 'numeric', minute: '2-digit', hour12: true };
|
||||
const dateStr = start.toLocaleDateString('en-US', optionsDate);
|
||||
const startTimeStr = start.toLocaleTimeString('en-US', optionsTime);
|
||||
const endTimeStr = end.toLocaleTimeString('en-US', optionsTime);
|
||||
return `${dateStr} • ${startTimeStr} – ${endTimeStr}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = async function() {
|
||||
try {
|
||||
const eventsListUrl = `${zohoApiUrl}/events?range={"start":"${startDate}","end":"${endDate}"}`;
|
||||
console.log(`Making initial API call to: ${eventsListUrl}`);
|
||||
console.log(`Using access token`);
|
||||
|
||||
let response = await fetch(eventsListUrl, {
|
||||
headers: { Authorization: `Zoho-oauthtoken ${accessToken}` }
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
console.log("Access token is expired, refreshing...");
|
||||
await refreshAccessToken();
|
||||
response = await fetch(eventsListUrl, {
|
||||
headers: { Authorization: `Zoho-oauthtoken ${accessToken}` }
|
||||
});
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`API call failed: ${response.status} ${response.statusText} - ${text}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Initial API call successful. Received data:");
|
||||
console.dir(data, { depth: null });
|
||||
|
||||
const normalizedEventsPromises = data.events.map(async (event) => {
|
||||
const basicEvent = {
|
||||
calid: event.calid,
|
||||
title: event.title,
|
||||
uid: event.uid,
|
||||
start: event.dateandtime.start,
|
||||
end: event.dateandtime.end,
|
||||
isallday: event.isallday,
|
||||
location: event.location,
|
||||
displayDate: formatEventDate(event.dateandtime.start, event.dateandtime.end, event.isallday),
|
||||
};
|
||||
|
||||
console.log(`Processing event with UID: ${basicEvent.uid}`);
|
||||
|
||||
if (basicEvent.uid) {
|
||||
const eventDetailsUrl = `${zohoApiUrl}/events/${basicEvent.uid}`;
|
||||
console.log(`Making details API call to: ${eventDetailsUrl}`);
|
||||
|
||||
const detailsResponse = await fetch(eventDetailsUrl, {
|
||||
headers: { Authorization: `Zoho-oauthtoken ${accessToken}` }
|
||||
});
|
||||
|
||||
if (detailsResponse.ok) {
|
||||
const detailsData = await detailsResponse.json();
|
||||
console.log(`Details API call for UID ${basicEvent.uid} successful. Received data:`, detailsData);
|
||||
|
||||
if (detailsData.events && detailsData.events.length > 0) {
|
||||
basicEvent.description = detailsData.events[0].description;
|
||||
console.log(`Description added for event UID: ${basicEvent.uid}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Failed to fetch details for event UID: ${basicEvent.uid}`);
|
||||
console.log(`Details response status: ${detailsResponse.status}`);
|
||||
}
|
||||
}
|
||||
return basicEvent;
|
||||
});
|
||||
|
||||
const normalizedEvents = await Promise.all(normalizedEventsPromises);
|
||||
console.log("All events processed. Final normalized events array:", normalizedEvents);
|
||||
return normalizedEvents;
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching Zoho events:", error);
|
||||
return [];
|
||||
}
|
||||
};
|
@@ -25,7 +25,7 @@
|
||||
<a href="/about/" {% if currentPage == 'about'%}class="current-page" aria-current="page"{% endif %}>About Us</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/community/" {% if currentPage == 'community' %}class="current-page" aria-current="page"{% endif %}>Community Events</a>
|
||||
<a href="/community/" {% if currentPage == 'community' %}class="current-page community-link" aria-current="page"{% endif %}>Community Events</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
196
src/about.njk
196
src/about.njk
@@ -15,102 +15,106 @@ pageScripts:
|
||||
- "/scripts/newsletter.js"
|
||||
---
|
||||
|
||||
<div id="main-top">
|
||||
<aside
|
||||
id="business-info"
|
||||
aria-label="Hours of operation and contact info"
|
||||
>
|
||||
<h3>Contact Us</h3>
|
||||
<div id="phone">
|
||||
<h4><strong>Phone:</strong></h4>
|
||||
<p>(555) 123-4567</p>
|
||||
</div>
|
||||
<h3>Hours of Operation</h3>
|
||||
<div id="hours">
|
||||
<h4><strong>Monday - Friday:</strong></h4>
|
||||
<p>9:00 AM - 6:00 PM</p>
|
||||
<h4><strong>Saturday & Sunday:</strong></h4>
|
||||
<p>10:00 AM - 5:00 PM</p>
|
||||
</div>
|
||||
</aside>
|
||||
<div id="story" aria-label="History of Bloom Valley Nursery">
|
||||
<h1>The Story of Bloom Valley</h1>
|
||||
<p>
|
||||
Legend has it that in 1822, Benjamin Bloom, his wife Violet, and
|
||||
their young daughter Nora stumbled upon a breathtaking valley
|
||||
brimming with vibrant, rare flowers during their journey westward.
|
||||
Enchanted by its beauty, they decided to settle there, abandoning
|
||||
their plans to reach California. This picturesque haven became known
|
||||
as Bloom Valley.
|
||||
</p>
|
||||
<p>
|
||||
Today, on the very land where the Bloom family found their paradise,
|
||||
stands Bloom Valley Nursery. Still family-owned and operated,
|
||||
siblings Bethany, Vincent, and Nathaniel Bloom carry forward a
|
||||
legacy of preserving and sharing the natural beauty that captivated
|
||||
the hearts of their ancestors over two centuries ago.
|
||||
</p>
|
||||
<p>Though many things have changed through the years, our commitment to serving our community has remained the same. Let us help you cultivate joy, one bloom at a time!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-bottom">
|
||||
<div id="feedback" aria-label="Feedback form">
|
||||
<div id="feedback-intro">
|
||||
<h1>We Want to Hear From You!</h1>
|
||||
<h3>Your feedback provides us with valuable insight into where we stand in our commitment to our neighbors, friends, and the entire Bloom Valley community.</h3>
|
||||
<p>Feel free to contact us by filling out the form below if you have any questions, concerns, or simply want to tell us how we can better serve you in the future. You are also welcome to give us a call at the number above if you need to speak with someone directly. Our friendly staff can help answer your questions or guide you in placing custom orders as well!</p>
|
||||
</div>
|
||||
<div id="feedback-form">
|
||||
<form action="#" method="POST">
|
||||
<div id="customer-info">
|
||||
<div id="name-field">
|
||||
<label for="name">Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
aria-label="Enter your name here" placeholder="Your Name"
|
||||
required/>
|
||||
</div>
|
||||
<div id="phone-field">
|
||||
<label for="tel">Phone Number:</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="tel"
|
||||
name="telephone"
|
||||
aria-label="Enter your contact phone number here" placeholder="Your Phone Number"
|
||||
required/>
|
||||
</div>
|
||||
<div id="email-field">
|
||||
<label for="email">Email:</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
aria-label="Enter your contact email here" placeholder="user@domain.com"
|
||||
required/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="customer-message">
|
||||
<div id="message-field">
|
||||
<label for="message">Message:</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows="5"
|
||||
aria-label="Enter feedback or inquiries here" placeholder="Enter feedback or inquiries here..."
|
||||
required>
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="form-submit-btn">
|
||||
<button
|
||||
id="form-submit"
|
||||
type="submit"
|
||||
aria-label="Submit feedback or inquiry"
|
||||
>Submit
|
||||
</button>
|
||||
</div>
|
||||
<div class="about">
|
||||
<div id="main-top">
|
||||
<section
|
||||
id="business-info"
|
||||
aria-label="Hours of operation and contact info"
|
||||
>
|
||||
<h3>Contact Us</h3>
|
||||
<div id="phone">
|
||||
<h4><strong>Phone:</strong></h4>
|
||||
<p>(555) 123-4567</p>
|
||||
</div>
|
||||
</form>
|
||||
<h3>Hours of Operation</h3>
|
||||
<div id="hours">
|
||||
<h4><strong>Monday - Friday:</strong></h4>
|
||||
<p>9:00 AM - 6:00 PM</p>
|
||||
<h4><strong>Saturday & Sunday:</strong></h4>
|
||||
<p>10:00 AM - 5:00 PM</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="story-section">
|
||||
<div id="story" aria-label="History of Bloom Valley Nursery">
|
||||
<h2>The Story of Bloom Valley</h2>
|
||||
<p>
|
||||
Legend has it that in 1822, Benjamin Bloom, his wife Violet, and
|
||||
their young daughter Nora stumbled upon a breathtaking valley
|
||||
brimming with vibrant, rare flowers during their journey westward.
|
||||
Enchanted by its beauty, they decided to settle there, abandoning
|
||||
their plans to reach California. This picturesque haven became known
|
||||
as Bloom Valley.
|
||||
</p>
|
||||
<p>
|
||||
Today, on the very land where the Bloom family found their paradise,
|
||||
stands Bloom Valley Nursery. Still family-owned and operated,
|
||||
siblings Bethany, Vincent, and Nathaniel Bloom carry forward a
|
||||
legacy of preserving and sharing the natural beauty that captivated
|
||||
the hearts of their ancestors over two centuries ago.
|
||||
</p>
|
||||
<p>Though many things have changed through the years, our commitment to serving our community has remained the same. Let us help you cultivate joy, one bloom at a time!</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div id="main-bottom">
|
||||
<div id="feedback" aria-label="Feedback form">
|
||||
<div id="feedback-intro">
|
||||
<h2>We Want to Hear From You!</h2>
|
||||
<h3>Your feedback provides us with valuable insight into where we stand in our commitment to our neighbors, friends, and the entire Bloom Valley community.</h3>
|
||||
<p>Feel free to contact us by filling out the form below if you have any questions, concerns, or simply want to tell us how we can better serve you in the future. You are also welcome to give us a call at the number above if you need to speak with someone directly. Our friendly staff can help answer your questions or guide you in placing custom orders as well!</p>
|
||||
</div>
|
||||
<div id="feedback-form">
|
||||
<form action="#" method="POST">
|
||||
<div id="customer-info">
|
||||
<div id="name-field">
|
||||
<label for="name">Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
aria-label="Enter your name here" placeholder="Your Name"
|
||||
required/>
|
||||
</div>
|
||||
<div id="phone-field">
|
||||
<label for="tel">Phone Number:</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="tel"
|
||||
name="telephone"
|
||||
aria-label="Enter your contact phone number here" placeholder="Your Phone Number"
|
||||
required/>
|
||||
</div>
|
||||
<div id="email-field">
|
||||
<label for="email">Email:</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
aria-label="Enter your contact email here" placeholder="user@domain.com"
|
||||
required/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="customer-message">
|
||||
<div id="message-field">
|
||||
<label for="message">Message:</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows="5"
|
||||
aria-label="Enter feedback or inquiries here" placeholder="Enter feedback or inquiries here..."
|
||||
required>
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="form-submit-btn">
|
||||
<button
|
||||
id="form-submit"
|
||||
type="submit"
|
||||
aria-label="Submit feedback or inquiry"
|
||||
>Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -13,87 +13,39 @@ pageScripts:
|
||||
- "/scripts/cart.js"
|
||||
- "/scripts/newsletter.js"
|
||||
---
|
||||
|
||||
<div class="community">
|
||||
<h1>Mark Your Calendars!</h1>
|
||||
<div id="calendar-events-container">
|
||||
<div class="calendar">
|
||||
<iframe
|
||||
src="https://calendar.google.com/calendar/embed?src=your_calendar_id&ctz=America/Chicago"
|
||||
style="border: 0; width: 90vww; height: 600px"
|
||||
frameborder="0"
|
||||
scrolling="no" aria-label="Google Calendar displaying upcoming events at Bloom Valley Nursery">
|
||||
</iframe><!-- change "your_calendar_id" to an actual Google Calendar ID (Not possible due to the Client Scenario being fictional. This would require creating a fake Google account.)-->
|
||||
</div>
|
||||
<section class="events" aria-label="List of upcoming local events">
|
||||
<h2>Upcoming Events</h2>
|
||||
<div class="event-list">
|
||||
<div id="bvn-events">
|
||||
<h3>Events at Bloom Valley Nursery</h3>
|
||||
<p>
|
||||
All workshops at Bloom Valley Nursery are provided free of charge to
|
||||
anyone interested in learning techniques and ideas for creating a
|
||||
visually appealing living space that can be personalized to suite
|
||||
individual needs. Registration links are provided in each Google
|
||||
Calendar Event by clicking on the date of the event within the
|
||||
Google Calendar on this page.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Gardening and Landscaping 101:</strong><br><strong>When:</strong><br> 2nd Tuesday of
|
||||
Every Month @ 6:30 pm.<br>Join Bethany Bloom as she shares her tips for
|
||||
creating a themed decor through-out the home that you will be
|
||||
delighted to share with your holiday guests.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Deck the Halls</strong><br><strong>When:</strong> Saturday @ 10:00 am.<br>Join
|
||||
Nathaniel and Vincent as they guide us through picking and tending
|
||||
the perfect live Christmas tree for your Christmas decoration
|
||||
focal-point.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Christmas Eve and Christmas Day</strong><br><strong>Bloom Valley
|
||||
Nursery will close on Christmas Eve at 8:00 pm</strong> for any last-minute
|
||||
shopping.<br>The nursery will remain <strong>closed until January 2nd</strong>. During
|
||||
this time, we will be making improvements to the nursery grounds and
|
||||
store with the aim of better serving our Bloom Valley neighbors,
|
||||
friends and family. We are excited to share the new features and changes with
|
||||
you in the New Year!<br><strong>Happy Holidays!!!</strong>
|
||||
</li>
|
||||
</ul>
|
||||
<section class="events" aria-label="List of upcoming local events">
|
||||
<div class="upcoming-events">
|
||||
<h2>Upcoming Events</h2>
|
||||
{% for event in zohoCalendarEvents %}
|
||||
<div id="{{ event.uid }}" class="zoho-event">
|
||||
{% if event.title %}
|
||||
<h3 class="title">{{ event.title }}</h3>
|
||||
{% endif %}
|
||||
<p class="display-date">{{ event.displayDate }}</p>
|
||||
{% if event.location %}
|
||||
<p class="location">Location: {{ event.location }}</p>
|
||||
{% endif %}
|
||||
{% if event.description %}
|
||||
<p class="description">{{ event.description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="community-wide">
|
||||
<h3>Community-Wide Events</h3>
|
||||
<p>
|
||||
This section includes local community events for which Bloom Valley
|
||||
Nursery is in partner with or a sponsor of. These events are
|
||||
typically free of charge, but can include sub-events by other local
|
||||
organizations and businesses that may not be free of charge. Check
|
||||
with specific organizations, businesses, and the Bloom Valley
|
||||
Chamber of Commerce for specific details that may not be provided by
|
||||
Bloom Valley Nursery.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Tree Lighting Ceremony</strong><br><strong>When:</strong></br>Thursday @ 6:00 pm.<br>
|
||||
Help us kick off the Holiday festivities by joining the Bloom
|
||||
family, along with the Bloom Valley Chamber of Commerce, the Bloom
|
||||
Valley Garden Club, and many other local businesses and
|
||||
organizations, as we gather <strong>in front of City Hall</strong> for the annual Tree
|
||||
Lighting Ceremony! This event will include performances by the Bloom
|
||||
Valley Junior and Senior High Marching Bands, a workshop for making
|
||||
tree ornaments, the annual Chili Cook-off, and much more!
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Add more events as needed -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="calendar">
|
||||
<iframe src="https://calendar.zoho.com/zc/ui/embed/#calendar=zz0801123006fa33fd29e1610f845b1a39bac3a52e92215b45d6a6f982e3a69c351d4e7b0be038133d1988f6ed212d5f2bd86e7bef&title=Bloom%20Valley%20Nursery&type=1&language=en&timezone=America%2FChicago&showTitle=1&showTimezone=1&startingDayOfWeek=0&timeFormat=0&view=month&showDetail=1&theme=1&showAttendee=0&showSwitchingViews=0&expandAllday=0&eventColorType=light&showAllEvents=0" title="Bloom Valley Nursery"width=650 height=500 frameBorder="0" scrolling="no"></iframe>
|
||||
</div>
|
||||
</section>
|
||||
<p>
|
||||
For more details, to add events to this page, or to leave feedback,
|
||||
<a
|
||||
href="/about/#feedback"
|
||||
aria-label="Go to the feedback form on the About Us page to leave feedback or inquiries."
|
||||
>click here</a
|
||||
> to send us a message.
|
||||
</p>
|
||||
</section>
|
||||
<div class="add-events">
|
||||
<p>
|
||||
For more details, to add events to this page, or to leave feedback,
|
||||
<a
|
||||
href="/about/#feedback"
|
||||
aria-label="Go to the feedback form on the About Us page to leave feedback or inquiries."
|
||||
>click here</a
|
||||
> to send us a message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
@@ -16,56 +16,32 @@ pageScripts:
|
||||
- "/scripts/newsletter.js"
|
||||
---
|
||||
|
||||
<!-- "Credits & Attributions" Template -->
|
||||
<h1>Credits & Attributions</h1>
|
||||
<p>A heartfelt <strong>Thank You</strong> is extended to these creators and the platforms that host their work. Their royalty-free contributions to the public helped make this demo possible.</p>
|
||||
<p id="tip-desc" class="visually-hidden">Preview shows a small version of the linked image.</p>
|
||||
<div class="credits-container">
|
||||
<div class="intro">
|
||||
<h1>Credits & Attributions</h1>
|
||||
<p>A heartfelt <strong>Thank You</strong> is extended to these creators and the platforms that host their work. Their royalty-free contributions to the public helped make this demo possible.</p>
|
||||
<p id="tip-desc" class="visually-hidden">Preview shows a small version of the linked image.</p>
|
||||
|
||||
<div>
|
||||
<ul class="credits-list">
|
||||
<!-- single credit -->
|
||||
<li class="credit">
|
||||
<!-- inline fallback (always in DOM, hidden when JS enhances) -->
|
||||
<div class="thumb">
|
||||
<img class="thumb-inline"
|
||||
src=""
|
||||
alt=""
|
||||
width="40" height="24"
|
||||
loading="lazy" decoding="async">
|
||||
</div>
|
||||
<div>
|
||||
<ul class="credits-list">
|
||||
{% for credit in credits.credits %}
|
||||
<li class="credit" data-tooltip="../images/_m-thumbs/{{ credit.category }}/{{ credit.file_name }}">
|
||||
<div class="thumb">
|
||||
<img class="thumb-inline"
|
||||
src="../images/_s-thumbs/{{ credit.category }}/{{ credit.file_name }}"
|
||||
alt="Attributed image for {{ credit.file_name }}"
|
||||
width="40" height="24"
|
||||
loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<p>
|
||||
Photo by
|
||||
<a class="creator" href=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Creator Name
|
||||
</a>
|
||||
at
|
||||
<a class="source" href=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Source Name
|
||||
</a>
|
||||
under
|
||||
<a class="license" href=""
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
License
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p class="small">
|
||||
<a class="source-link"
|
||||
href="" <!-- URL to original -->
|
||||
data-tooltip="" <!-- path to tooltip image -->
|
||||
aria-describedby="tip-desc"
|
||||
aria-haspopup="true" rel="noopener">
|
||||
View Original Here
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="meta" >
|
||||
<p>
|
||||
Photo by <a class="creator" href="{{ credit.creator_url }}" target="_blank"
|
||||
rel="noopener noreferrer">{{ credit.creator }}</a> at <a class="source" href="{{ credit.src_url }}" target="_blank" rel="noopener noreferrer">{{ credit.host }}</a> under the <a class="license" href="{{ credit.license_url }}" target="_blank" rel="noopener noreferrer">{{ credit.license }}</a> .
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
@@ -1,42 +1,47 @@
|
||||
// JS for tooltip hover
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Hide inline thumbs when JS is available
|
||||
// Hide inline thumbs when JS is active
|
||||
document.querySelectorAll(".thumb-inline").forEach(img => img.style.display = "none");
|
||||
|
||||
// Tooltip element
|
||||
const tip = document.createElement("div");
|
||||
tip.className = "thumb-tooltip";
|
||||
document.body.appendChild(tip);
|
||||
|
||||
function positionTip(el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const tipRect = tip.getBoundingClientRect();
|
||||
let left = rect.left + window.scrollX;
|
||||
if (left + tipRect.width > window.innerWidth) {
|
||||
left = window.innerWidth - tipRect.width - 8; // small margin
|
||||
}
|
||||
tip.style.top = (rect.bottom + window.scrollY + 6) + "px";
|
||||
tip.style.left = left + "px";
|
||||
}
|
||||
|
||||
|
||||
function show(e) {
|
||||
const url = e.currentTarget.getAttribute("data-tooltip");
|
||||
const el = e.currentTarget;
|
||||
const url = el.getAttribute("data-tooltip");
|
||||
if (!url) return;
|
||||
|
||||
// Preload image to get natural size
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
tip.style.width = img.naturalWidth + "px";
|
||||
tip.style.height = img.naturalHeight + "px";
|
||||
tip.style.backgroundImage = `url("${url}")`;
|
||||
tip.style.display = "block";
|
||||
positionTip(e.currentTarget);
|
||||
|
||||
// preload image to size tooltip
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
tip.style.width = img.naturalWidth + "px";
|
||||
tip.style.height = img.naturalHeight + "px";
|
||||
|
||||
// position tooltip relative to element
|
||||
const rect = el.getBoundingClientRect();
|
||||
const scrollX = window.scrollX;
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
let left = rect.left + scrollX + (rect.width - tip.offsetWidth) / 2;
|
||||
let top = rect.top + scrollY - tip.offsetHeight - 8; // above element
|
||||
|
||||
// fallback below if not enough space
|
||||
if (top < scrollY) {
|
||||
top = rect.bottom + scrollY + 8;
|
||||
}
|
||||
|
||||
tip.style.left = left + "px";
|
||||
tip.style.top = top + "px";
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
function hide() { tip.style.display = "none"; }
|
||||
function hide() {
|
||||
tip.style.display = "none";
|
||||
}
|
||||
|
||||
document.querySelectorAll("li.credit").forEach(item => {
|
||||
item.addEventListener("mouseenter", show);
|
||||
@@ -44,4 +49,4 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
item.addEventListener("focusin", show);
|
||||
item.addEventListener("focusout", hide);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* |--↓-↓-↓ Start about.html ↓-↓-↓--| */
|
||||
#about-page main {
|
||||
.about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -8,9 +8,9 @@
|
||||
#main-top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 95vw;
|
||||
position: center;
|
||||
gap: 30px;
|
||||
width: 90vw;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#business-info {
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
|
||||
#business-info h3,
|
||||
#feedback-intro h1 {
|
||||
#feedback-intro h2 {
|
||||
font-size: 1.875rem;
|
||||
text-align: center;
|
||||
text-shadow: 0.5px 0.5px 2px #014038, /* Bottom-right base shadow */
|
||||
@@ -77,7 +77,7 @@ box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
#story h1 {
|
||||
#story h2 {
|
||||
background-color: #96baa0;
|
||||
color: #f7f7f7;
|
||||
border: solid #f7f7f7;
|
||||
@@ -112,7 +112,7 @@ box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#feedback h1 {
|
||||
#feedback h2 {
|
||||
background-color: #a4ccd6;
|
||||
border: solid #f7f7f7;
|
||||
border-radius: 10px;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
* Global Styles */
|
||||
/* Global Styles */
|
||||
html {
|
||||
font-size: 17px; /* Base font size for scaling */
|
||||
scroll-behavior: smooth;
|
||||
|
@@ -1,90 +1,143 @@
|
||||
/* |--↓-↓-↓ Start Community.html ↓-↓-↓--| */
|
||||
#community-page {
|
||||
.community {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
width: 95vh;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
#community-page h1 {
|
||||
font-size: 1.6rem;
|
||||
background-color: #96baa0;
|
||||
border: solid #f7f7f7;
|
||||
h1 {
|
||||
display: block;
|
||||
font-size: 2.3rem;
|
||||
background-color: #f7f7f7;
|
||||
border: solid #96baa0;
|
||||
border-radius: 15px;
|
||||
width: 50vw;
|
||||
color: #f7f7f7;
|
||||
color: #014038;
|
||||
text-decoration: underline;
|
||||
text-shadow: 0.5px 0.5px 1px #014038, /* Bottom-right base shadow */
|
||||
-0.5px -0.5px 1px #014038, /* Top-left base shadow */
|
||||
-0.5px 0.5px 1px #014038, /* Bottom-left base shadow */
|
||||
0.5px -0.5px 1px #014038; /* Top-right base shadow */
|
||||
padding: 5px 10px;
|
||||
padding: 5px 100px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#calendar-events-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.calendar iframe{
|
||||
width: 80vw;
|
||||
height: auto;
|
||||
width: 975px;
|
||||
height: 750px;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #bdd4da;
|
||||
border-radius: 15px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.upcoming-events {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.events {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
background-color: #f0c06d;
|
||||
border: solid #014038;
|
||||
border-radius: 15px;
|
||||
padding: 10px;
|
||||
width: 95vw;
|
||||
width: 80vw;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.events h2 {
|
||||
font-size: 1.875rem;
|
||||
text-align: center;
|
||||
text-shadow: 0.5px 0.5px .5px #014038, /* Bottom-right top shadow */
|
||||
-0.5px -0.5px .5px #014038, /* Top-left top shadow */
|
||||
-0.5px 0.5px .5px #014038, /* Bottom-left top shadow */
|
||||
0.5px -0.5px .5px #014038; /* Top-right top shadow */
|
||||
color: #f7f7f7;
|
||||
background-color: #96baa0;
|
||||
border: solid #f7f7f7;
|
||||
border-radius: 15px;
|
||||
padding: 10px 15px;
|
||||
width: 50vw;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
text-align: center;
|
||||
margin-left: 20vw;
|
||||
transform: translateY(-6vh);
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: fit-content;
|
||||
padding: 0 50px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.events h3 {
|
||||
background-color: #96baa080;
|
||||
border: solid #bdd4da;
|
||||
border-radius: 15px;
|
||||
padding: 5px 10px;
|
||||
width: 50vw;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
.upcoming-events {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin-left: 20vw;
|
||||
transform: translateY(-5vh);
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
background-color: #CADCD0;
|
||||
border: solid #014038;
|
||||
border-radius: 15px;
|
||||
padding: 0px 20px 10px 20px;
|
||||
width: 95%;
|
||||
margin-top: 20px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
}
|
||||
|
||||
.events p {
|
||||
transform: translateY(-5vh);
|
||||
.zoho-event {
|
||||
background-color:#f7f7f7;
|
||||
border: solid #014038 2px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.8);
|
||||
max-width: 1200px;
|
||||
|
||||
}
|
||||
|
||||
.zoho-event h3 {
|
||||
font-size: 1.875rem;
|
||||
text-align: center;
|
||||
text-shadow: 0.5px 0.5px .5px #f7f7f7, /* Bottom-right top shadow */
|
||||
-0.5px -0.5px .5px #f7f7f7, /* Top-left top shadow */
|
||||
-0.5px 0.5px .5px #f7f7f7, /* Bottom-left top shadow */
|
||||
0.5px -0.5px .5px #f7f7f7; /* Top-right top shadow */
|
||||
color: #014038;
|
||||
background-color: #bdd4da;
|
||||
border: solid #f7f7f7;
|
||||
border-radius: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: fit-content;
|
||||
padding: 0 50px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.zoho-event p {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.display-date, .location, .add-events {
|
||||
font-weight: heavy;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.3rem;
|
||||
width: 80%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.event-list ul {
|
||||
list-style-type: none;
|
||||
transform: translateY(-5vh);
|
||||
|
||||
}
|
||||
|
||||
.event-list li {
|
||||
|
@@ -1,4 +1,19 @@
|
||||
/* CSS Style Rules for "Credits & Attributions" */
|
||||
.credits-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#tip-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.credits-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
@@ -40,10 +55,13 @@
|
||||
.thumb-tooltip { /* background image is handled by JS */
|
||||
position: absolute;
|
||||
display: none;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain; /* fit full image */
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
border: 1px solid #ccc;
|
||||
border: 3px solid #555;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||||
z-index: 9999;
|
||||
}
|
@@ -90,7 +90,7 @@ header .logo img {
|
||||
.business-name {
|
||||
font-family: 'Georgia', serif;
|
||||
display: block;
|
||||
font-size: 2.8rem;
|
||||
font-size: 3.6rem;
|
||||
color: #014038; /* Dark green for the title */
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
@@ -266,6 +266,10 @@ nav ul li, #footer-nav ul li {
|
||||
box-shadow: 1px 2px 3px 0.5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.community-link {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
nav ul li a, #footer-nav ul li a {
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-size: 1rem;
|
||||
@@ -280,7 +284,8 @@ nav ul li a, #footer-nav ul li a {
|
||||
nav ul li a:hover, #footer-nav ul li a:hover {
|
||||
background-color: #f7f7f7;
|
||||
color: #014038;
|
||||
border: solid #014038;
|
||||
border: solid .5px #014038;
|
||||
padding: 7px 14px;
|
||||
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user