Compare commits
10 Commits
e0de86bb5a
...
9287b4da1a
Author | SHA1 | Date | |
---|---|---|---|
9287b4da1a | |||
896bb5242a | |||
9a32b8dfc0 | |||
a9437816e1 | |||
db3d9fa9f4 | |||
7430c165aa | |||
1766431695 | |||
879a56d98e | |||
982a376264 | |||
7cad2d9c13 |
311
package-lock.json
generated
@ -1,18 +1,22 @@
|
||||
{
|
||||
"name": "tailwindui-template",
|
||||
"name": "wscc-website",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tailwindui-template",
|
||||
"name": "wscc-website",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@headlessui/react": "1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/mdx": "13.4.16",
|
||||
"@stripe/react-stripe-js": "2.4.0",
|
||||
"@stripe/stripe-js": "2.2.1",
|
||||
"@tailwindcss/forms": "0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@types/node": "20.4.7",
|
||||
"@types/react": "18.2.18",
|
||||
@ -25,9 +29,11 @@
|
||||
"feed": "^4.2.2",
|
||||
"next": "13.4.16",
|
||||
"next-themes": "^0.2.1",
|
||||
"postmark": "4.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"stripe": "14.9.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
@ -128,9 +134,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "1.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.15.tgz",
|
||||
"integrity": "sha512-OTO0XtoRQ6JPB1cKNFYBZv2Q0JMqMGNhYP1CjPvcJvjz8YGokz8oAj89HIYZGN0gZzn/4kk9iUpmMF4Q21Gsqw==",
|
||||
"version": "1.7.17",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
|
||||
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
|
||||
"dependencies": {
|
||||
"client-only": "^0.0.1"
|
||||
},
|
||||
@ -142,6 +148,14 @@
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroicons/react": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz",
|
||||
"integrity": "sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
||||
@ -556,6 +570,24 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/react-stripe-js": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.4.0.tgz",
|
||||
"integrity": "sha512-1jVQEL3OuhuzNlf4OdfqovHt+MkWh8Uh8xpLxx/xUFUDdF+7/kDOrGKy+xJO3WLCfZUL7NAy+/ypwXbbYZi0tg==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@stripe/stripe-js": "^1.44.1 || ^2.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.2.1.tgz",
|
||||
"integrity": "sha512-Wd81A0u8EwT3cf+Xv1mpMI18RbXVhgh19MtPcF9ojNTlG3kl36B1+XFe1KQfnJxD3WRnVfDuI0rNCK53mcGm6g=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
||||
@ -575,6 +607,17 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/forms": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz",
|
||||
"integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==",
|
||||
"dependencies": {
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||
@ -1258,6 +1301,11 @@
|
||||
"astring": "bin/astring"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.14",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
||||
@ -1311,6 +1359,16 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
|
||||
@ -1674,7 +1732,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
@ -2044,6 +2101,17 @@
|
||||
"simple-swizzle": "^0.2.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==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/comma-separated-tokens": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
||||
@ -2310,6 +2378,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
@ -3405,6 +3481,25 @@
|
||||
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
@ -3414,6 +3509,19 @@
|
||||
"is-callable": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@ -3515,7 +3623,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@ -3827,7 +3934,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -3839,7 +3945,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -6668,7 +6773,6 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@ -6677,7 +6781,6 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@ -6705,6 +6808,14 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-svg-data-uri": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
|
||||
"bin": {
|
||||
"mini-svg-data-uri": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -7120,7 +7231,6 @@
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -7676,6 +7786,14 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
},
|
||||
"node_modules/postmark": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postmark/-/postmark-4.0.2.tgz",
|
||||
"integrity": "sha512-2zlCv+KVVQ0KoamXZHE7d+gXzLlr8tPE+PxQmtUaIZhbHzZAq4D6yH2b+ykhA8wYCc5ISodcx8U1aNLenXBs9g==",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||
@ -7854,7 +7972,6 @@
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -7870,6 +7987,11 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@ -7903,6 +8025,20 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@ -7985,8 +8121,7 @@
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
@ -8626,7 +8761,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
@ -8985,6 +9119,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "14.9.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-14.9.0.tgz",
|
||||
"integrity": "sha512-t2XdpNbRH4x3MYEoxNWhwUPl9D80aUd5OHds0zhDiwRYPZ0H7MrUI/dj9wOSYlzycD3xdvjn0q7pWeFWljtMUQ==",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-object": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz",
|
||||
@ -10410,13 +10556,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"@headlessui/react": {
|
||||
"version": "1.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.15.tgz",
|
||||
"integrity": "sha512-OTO0XtoRQ6JPB1cKNFYBZv2Q0JMqMGNhYP1CjPvcJvjz8YGokz8oAj89HIYZGN0gZzn/4kk9iUpmMF4Q21Gsqw==",
|
||||
"version": "1.7.17",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.17.tgz",
|
||||
"integrity": "sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==",
|
||||
"requires": {
|
||||
"client-only": "^0.0.1"
|
||||
}
|
||||
},
|
||||
"@heroicons/react": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz",
|
||||
"integrity": "sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
||||
@ -10680,6 +10832,19 @@
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
|
||||
},
|
||||
"@stripe/react-stripe-js": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.4.0.tgz",
|
||||
"integrity": "sha512-1jVQEL3OuhuzNlf4OdfqovHt+MkWh8Uh8xpLxx/xUFUDdF+7/kDOrGKy+xJO3WLCfZUL7NAy+/ypwXbbYZi0tg==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@stripe/stripe-js": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.2.1.tgz",
|
||||
"integrity": "sha512-Wd81A0u8EwT3cf+Xv1mpMI18RbXVhgh19MtPcF9ojNTlG3kl36B1+XFe1KQfnJxD3WRnVfDuI0rNCK53mcGm6g=="
|
||||
},
|
||||
"@swc/helpers": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
||||
@ -10696,6 +10861,14 @@
|
||||
"defer-to-connect": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/forms": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz",
|
||||
"integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==",
|
||||
"requires": {
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/typography": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||
@ -11256,6 +11429,11 @@
|
||||
"resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz",
|
||||
"integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "10.4.14",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
||||
@ -11281,6 +11459,16 @@
|
||||
"integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"axobject-query": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
|
||||
@ -11535,7 +11723,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
@ -11789,6 +11976,14 @@
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"comma-separated-tokens": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
||||
@ -11973,6 +12168,11 @@
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
@ -12802,6 +13002,11 @@
|
||||
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q=="
|
||||
},
|
||||
"for-each": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||
@ -12811,6 +13016,16 @@
|
||||
"is-callable": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fraction.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
@ -12880,7 +13095,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@ -13110,14 +13324,12 @@
|
||||
"has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||
},
|
||||
"has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
@ -15056,14 +15268,12 @@
|
||||
"mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"peer": true
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
||||
},
|
||||
"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==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
@ -15079,6 +15289,11 @@
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"mini-svg-data-uri": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@ -15375,8 +15590,7 @@
|
||||
"object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
|
||||
},
|
||||
"object-keys": {
|
||||
"version": "1.1.1",
|
||||
@ -15740,6 +15954,14 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
},
|
||||
"postmark": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postmark/-/postmark-4.0.2.tgz",
|
||||
"integrity": "sha512-2zlCv+KVVQ0KoamXZHE7d+gXzLlr8tPE+PxQmtUaIZhbHzZAq4D6yH2b+ykhA8wYCc5ISodcx8U1aNLenXBs9g==",
|
||||
"requires": {
|
||||
"axios": "^1.6.2"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||
@ -15828,7 +16050,6 @@
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -15840,6 +16061,11 @@
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz",
|
||||
"integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg=="
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@ -15867,6 +16093,14 @@
|
||||
"escape-goat": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.11.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
||||
"requires": {
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@ -15925,8 +16159,7 @@
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
@ -16379,7 +16612,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
@ -16628,6 +16860,15 @@
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true
|
||||
},
|
||||
"stripe": {
|
||||
"version": "14.9.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-14.9.0.tgz",
|
||||
"integrity": "sha512-t2XdpNbRH4x3MYEoxNWhwUPl9D80aUd5OHds0zhDiwRYPZ0H7MrUI/dj9wOSYlzycD3xdvjn0q7pWeFWljtMUQ==",
|
||||
"requires": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"style-to-object": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz",
|
||||
|
10
package.json
@ -10,11 +10,15 @@
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@headlessui/react": "1.7.17",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/mdx": "13.4.16",
|
||||
"@stripe/react-stripe-js": "2.4.0",
|
||||
"@stripe/stripe-js": "2.2.1",
|
||||
"@tailwindcss/forms": "0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@types/node": "20.4.7",
|
||||
"@types/react": "18.2.18",
|
||||
@ -27,9 +31,11 @@
|
||||
"feed": "^4.2.2",
|
||||
"next": "13.4.16",
|
||||
"next-themes": "^0.2.1",
|
||||
"postmark": "4.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"stripe": "14.9.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
@ -40,4 +46,4 @@
|
||||
"prettier-plugin-tailwindcss": "^0.5.2",
|
||||
"sharp": "^0.32.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
public/WSCC-Hall-Rental-Application.pdf
Executable file
BIN
public/WSCC-Hall-Rental-QA.pdf
Executable file
BIN
public/WSCC-Membership-Form.pdf
Executable file
62
src/app/api/stripe_intent/route.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import Stripe from 'stripe';
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || '');
|
||||
|
||||
export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||
const { amount, payment_intent_id, metadata } = await request.json();
|
||||
|
||||
if (payment_intent_id) {
|
||||
try {
|
||||
// If a payment_intent_id is passed, retrieve the paymentIntent
|
||||
const current_intent = await stripe.paymentIntents.retrieve(
|
||||
payment_intent_id,
|
||||
);
|
||||
// If a paymentIntent is retrieved update its amount
|
||||
if (current_intent) {
|
||||
const updated_intent = await stripe.paymentIntents.update(
|
||||
payment_intent_id,
|
||||
{
|
||||
amount: amount,
|
||||
metadata: metadata || {},
|
||||
},
|
||||
);
|
||||
|
||||
return NextResponse.json(updated_intent, { status: 200 });
|
||||
}
|
||||
} catch (e) {
|
||||
// Catch any error and return a status 500
|
||||
if ((e as any).code !== 'resource_missing') {
|
||||
const errorMessage =
|
||||
e instanceof Error ? e.message : 'Internal server error';
|
||||
return NextResponse.json(
|
||||
{ statusCode: 500, message: errorMessage },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Create PaymentIntent
|
||||
const params: Stripe.PaymentIntentCreateParams = {
|
||||
amount: amount,
|
||||
currency: 'usd',
|
||||
payment_method_types: ['card'],
|
||||
automatic_payment_methods: {
|
||||
enabled: false,
|
||||
},
|
||||
metadata: metadata || {},
|
||||
};
|
||||
const payment_intent = await stripe.paymentIntents.create(params);
|
||||
//Return the payment_intent object
|
||||
return NextResponse.json(payment_intent, { status: 200 });
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : 'Internal server error';
|
||||
return NextResponse.json(
|
||||
{ statusCode: 500, message: errorMessage },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
29
src/app/api/stripe_webhook/route.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { emailNotification } from '@/lib/email';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export async function POST(request: NextRequest): Promise<Response> {
|
||||
const body = await request.json();
|
||||
|
||||
if (body.type === 'charge.succeeded') {
|
||||
const data = body.data.object;
|
||||
const billing = data.billing_details;
|
||||
const cityStateZip = `${billing.address.city}, ${billing.address.state} ${billing.address.postal_code}`;
|
||||
const address = billing.address.line2
|
||||
? `${billing.address.line1}\n${cityStateZip}`
|
||||
: `${billing.address.line1}\n${billing.address.line2}\n${cityStateZip}`;
|
||||
|
||||
emailNotification({
|
||||
name: billing.name,
|
||||
type: data.metadata.type,
|
||||
amount: data.amount,
|
||||
email: billing.email,
|
||||
phone: billing.phone,
|
||||
address,
|
||||
});
|
||||
} else {
|
||||
console.log('Another type of event');
|
||||
console.log(body);
|
||||
}
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
@ -3,7 +3,7 @@ import Image from 'next/image'
|
||||
import designSystem from './planetaria-design-system.png'
|
||||
|
||||
export const article = {
|
||||
author: 'Adam Wathan',
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-09-05',
|
||||
title: 'Crafting a design system for a multiplanetary future',
|
||||
description:
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Adam Wathan',
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-09-02',
|
||||
title: 'Introducing Animaginary: High performance web animations',
|
||||
description:
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Adam Wathan',
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-07-14',
|
||||
title: 'Rewriting the cosmOS kernel in Rust',
|
||||
description:
|
||||
|
@ -1,14 +1,13 @@
|
||||
|
||||
import { type Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
InstagramIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import portraitImage from '@/images/portrait.jpg'
|
||||
import { UserPlusIcon, GiftIcon, EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
import interiorEmptyImage from '@/images/photos/interior-empty.jpg'
|
||||
import { ClubPayment } from './payment';
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
@ -34,23 +33,15 @@ function SocialLink({
|
||||
)
|
||||
}
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M6 5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V8a3 3 0 0 0-3-3H6Zm.245 2.187a.75.75 0 0 0-.99 1.126l6.25 5.5a.75.75 0 0 0 .99 0l6.25-5.5a.75.75 0 0 0-.99-1.126L12 12.251 6.245 7.187Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'History',
|
||||
description:
|
||||
'The West Sound Community Club on Orcas Island.',
|
||||
}
|
||||
|
||||
|
||||
// TODO: Replace interiorEmptyImage with a photo from a potluck
|
||||
|
||||
export default function Club() {
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
@ -58,7 +49,7 @@ export default function Club() {
|
||||
<div className="lg:pl-20">
|
||||
<div className="max-w-xs px-2.5 lg:max-w-none">
|
||||
<Image
|
||||
src={portraitImage}
|
||||
src={interiorEmptyImage}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="aspect-square rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
@ -71,30 +62,47 @@ export default function Club() {
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p>
|
||||
The West Sound Community Club, a nonprofit, tax-exempt corporation
|
||||
under Section 501(c)3 of the Internal Revenue Code, owns and manages
|
||||
the Hall. The Club sponsors monthly community potlucks at the Hall
|
||||
from September through May.
|
||||
The West Sound Community Club is a group of neighbors and friends
|
||||
living in the West Sound area. Together we steward the West Sound
|
||||
Hall, and gather monthly for potlucks and other community events.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Members of the West Sound community, past and present, have
|
||||
invested time, money, and talent in creating a true old-fashioned
|
||||
community center. For over one hundred years, the hall has
|
||||
provided continuity for the residents and guests of the West Sound
|
||||
community. The current residents continue the commitment to
|
||||
maintain this unique gathering place out of care and respect for
|
||||
past and future generations.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The West Sound Community Club is a nonprofit, tax-exempt corporation
|
||||
under Section 501(c)3 of the Internal Revenue Code.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:pl-20">
|
||||
<ul role="list">
|
||||
<SocialLink href="#" icon={TwitterIcon}>
|
||||
<SocialLink href="/WSCC-Membership-Form.pdf" icon={UserPlusIcon}>
|
||||
Membership Form
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={InstagramIcon} className="mt-4">
|
||||
Donations
|
||||
<SocialLink href="#" icon={GiftIcon} className="mt-4">
|
||||
Donations (Coming soon)
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={MailIcon}
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
>
|
||||
contact@westsoundhall.org
|
||||
</SocialLink>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ClubPayment />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
|
391
src/app/club/payment.tsx
Normal file
@ -0,0 +1,391 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect, useState, FormEvent } from 'react';
|
||||
import { AddressElement, Elements } from '@stripe/react-stripe-js';
|
||||
import { Appearance, StripeAddressElementOptions, StripeElementsOptions, loadStripe } from '@stripe/stripe-js';
|
||||
import {
|
||||
PaymentElement,
|
||||
useStripe,
|
||||
useElements,
|
||||
} from '@stripe/react-stripe-js';
|
||||
import { RadioGroup } from '@headlessui/react'
|
||||
|
||||
const stripe = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || '');
|
||||
|
||||
const addressOptions: StripeAddressElementOptions = {
|
||||
mode: 'billing',
|
||||
allowedCountries: ['US'],
|
||||
fields: {
|
||||
phone: 'always',
|
||||
},
|
||||
validation: {
|
||||
phone: {
|
||||
required: 'always'
|
||||
}
|
||||
},
|
||||
defaultValues: {
|
||||
address: {
|
||||
state: 'WA',
|
||||
country: 'US',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const membershipLevels = [
|
||||
{ id: 1, title: 'Individual', description: 'One vote', price: 20 },
|
||||
{ id: 2, title: 'Household', description: 'Two votes', price: 30 },
|
||||
];
|
||||
|
||||
const additionalDonationLevels = [10, 50, 100, -1];
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
function Spinner(): React.JSX.Element {
|
||||
return (<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M10.72,19.9a8,8,0,0,1-6.5-9.79A7.77,7.77,0,0,1,10.4,4.16a8,8,0,0,1,9.49,6.52A1.54,1.54,0,0,0,21.38,12h.13a1.37,1.37,0,0,0,1.38-1.54,11,11,0,1,0-12.7,12.39A1.54,1.54,0,0,0,12,21.34h0A1.47,1.47,0,0,0,10.72,19.9Z">
|
||||
<animateTransform attributeName="transform" dur="1s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12" />
|
||||
</path>
|
||||
</svg>);
|
||||
}
|
||||
|
||||
function CheckoutForm({
|
||||
paymentIntentID
|
||||
}: {
|
||||
paymentIntentID: string
|
||||
}): React.JSX.Element {
|
||||
const [selectedMembershipLevel, setSelectedMembershipLevel] = useState(membershipLevels[0]);
|
||||
const [selectedAdditionalDonation, setSelectedAdditionalDonation] = useState<number | null>(null);
|
||||
const [customAmount, setCustomAmount] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [offsetFees, setOffsetFees] = useState(true);
|
||||
const [totalAmount, setTotalAmount] = useState(300);
|
||||
const [message, setMessage] = useState<string>('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
|
||||
useEffect(() => {
|
||||
if (!stripe) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Grab the client secret from url params
|
||||
const clientSecret = new URLSearchParams(window.location.search).get(
|
||||
'payment_intent_client_secret'
|
||||
);
|
||||
|
||||
if (!clientSecret) {
|
||||
return;
|
||||
}
|
||||
|
||||
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
|
||||
switch (paymentIntent?.status) {
|
||||
case 'succeeded':
|
||||
setMessage('Payment succeeded!');
|
||||
break;
|
||||
case 'processing':
|
||||
setMessage('Your payment is processing.');
|
||||
break;
|
||||
case 'requires_payment_method':
|
||||
setMessage('Your payment was not successful, please try again.');
|
||||
break;
|
||||
default:
|
||||
setMessage('Something went wrong.');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [stripe]);
|
||||
|
||||
useEffect(() => {
|
||||
let subtotal = selectedMembershipLevel.price
|
||||
|
||||
if (selectedAdditionalDonation && selectedAdditionalDonation !== -1) {
|
||||
subtotal += selectedAdditionalDonation
|
||||
} else if (customAmount !== '') {
|
||||
try {
|
||||
subtotal += parseFloat(customAmount)
|
||||
} catch {
|
||||
console.error('')
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetFees) {
|
||||
subtotal = Math.ceil(subtotal * 1.03)
|
||||
}
|
||||
|
||||
setTotalAmount(subtotal);
|
||||
|
||||
fetch('api/stripe_intent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
amount: subtotal * 100,
|
||||
payment_intent_id: paymentIntentID,
|
||||
metadata: {
|
||||
'type': selectedMembershipLevel.title,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}, [paymentIntentID, selectedMembershipLevel, selectedAdditionalDonation, customAmount, offsetFees, email])
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!stripe || !elements) {
|
||||
// Stripe.js has not yet loaded.
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const { error } = await stripe.confirmPayment({
|
||||
elements,
|
||||
confirmParams: {
|
||||
return_url: window.location.origin + '/thank-you',
|
||||
receipt_email: email,
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
// Other details are filled automatically by address form.
|
||||
email,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (error.type === 'card_error' || error.type === 'validation_error') {
|
||||
setMessage(error.message || '');
|
||||
} else {
|
||||
setMessage('An unexpected error occured.');
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form id="payment-form" onSubmit={handleSubmit} className="m-auto space-y-4">
|
||||
|
||||
{/* Membership Type */}
|
||||
<RadioGroup value={selectedMembershipLevel} onChange={setSelectedMembershipLevel} className="space-y-3">
|
||||
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
|
||||
Select a membership type
|
||||
</RadioGroup.Label>
|
||||
|
||||
<div className="grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
|
||||
{membershipLevels.map((membership) => (
|
||||
<RadioGroup.Option
|
||||
key={membership.id}
|
||||
value={membership}
|
||||
className={({ active }) =>
|
||||
classNames(
|
||||
active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-300',
|
||||
'relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ checked, active }) => (
|
||||
<>
|
||||
<span className="flex flex-1 items-center justify-between ">
|
||||
<span className="flex flex-col">
|
||||
<RadioGroup.Label as="span" className="block text-sm font-medium text-gray-900">
|
||||
{membership.title}
|
||||
</RadioGroup.Label>
|
||||
<RadioGroup.Description as="span" className="mt-1 flex items-center text-sm text-gray-500">
|
||||
{membership.description}
|
||||
</RadioGroup.Description>
|
||||
</span>
|
||||
<RadioGroup.Description as="span" className="ml-8 text-sm font-medium">
|
||||
<span className="text-gray-900">${membership.price}</span>
|
||||
<span className="text-gray-500">/yr</span>
|
||||
</RadioGroup.Description>
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
active ? 'border' : 'border-2',
|
||||
checked ? 'border-indigo-600' : 'border-transparent',
|
||||
'pointer-events-none absolute -inset-px rounded-lg'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</RadioGroup.Option>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
{/* Additional donation */}
|
||||
<RadioGroup value={selectedAdditionalDonation} onChange={setSelectedAdditionalDonation} className="space-y-3">
|
||||
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
|
||||
Additional donation
|
||||
</RadioGroup.Label>
|
||||
<div className="grid grid-cols-3 gap-3 sm:grid-cols-5">
|
||||
{additionalDonationLevels.map((option) => (
|
||||
<RadioGroup.Option
|
||||
key={option}
|
||||
value={option}
|
||||
className={({ active, checked }) =>
|
||||
classNames(
|
||||
'cursor-pointer focus:outline-none',
|
||||
option === -1 ? 'col-span-2' : '',
|
||||
checked
|
||||
? 'ring-2 ring-indigo-600 ring-offset-2'
|
||||
: 'ring-1 ring-inset ring-gray-300 bg-white text-gray-900 hover:bg-gray-50',
|
||||
'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1'
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
{option === -1
|
||||
? (
|
||||
<RadioGroup.Label as="span" className="flex flex-col items-center">
|
||||
<div className="relative rounded-md shadow-sm">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span className="text-gray-500 sm:text-sm">$</span>
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
name="custom-amount"
|
||||
id="custom-amount"
|
||||
value={customAmount}
|
||||
min="0"
|
||||
step="1"
|
||||
onChange={(e) => setCustomAmount(e.target.value)}
|
||||
className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-500 sm:text-sm sm:leading-6"
|
||||
placeholder="Custom"
|
||||
aria-describedby="price-currency"
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<span className="text-gray-500 sm:text-sm" id="price-currency">
|
||||
/yr
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup.Label>
|
||||
)
|
||||
: (
|
||||
<RadioGroup.Label as="span">
|
||||
<span>${option}</span>
|
||||
<span className="text-gray-500">/yr</span>
|
||||
</RadioGroup.Label>
|
||||
)}
|
||||
</RadioGroup.Option>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h2 className="text-base font-semibold leading-6 text-gray-900">
|
||||
About you
|
||||
</h2>
|
||||
|
||||
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-indigo-600">
|
||||
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="block w-full border-0 p-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
<AddressElement options={addressOptions} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h2 className="text-base font-semibold leading-6 text-gray-900">
|
||||
Payment
|
||||
</h2>
|
||||
<div className="mt-1 text-sm text-gray-500">
|
||||
If you would like to pay by cash or check, please instead
|
||||
<a className="underline mx-1" href="/WSCC-Membership-Form.pdf">fill out a paper form</a>
|
||||
and mail to the address on the form.
|
||||
</div>
|
||||
<PaymentElement id="payment-element" />
|
||||
</div>
|
||||
|
||||
{/* TODO: Automatically renew toggle? */}
|
||||
|
||||
<div className="relative flex gap-x-3">
|
||||
<div className="flex h-6 items-center">
|
||||
<input
|
||||
id="offsetFees"
|
||||
name="offsetFees"
|
||||
type="checkbox"
|
||||
checked={offsetFees}
|
||||
onChange={(e) => setOffsetFees(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm leading-6">
|
||||
<label htmlFor="offsetFees" className="font-medium text-gray-900">
|
||||
Help offset credit card fees
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="mt-6 w-full rounded-md flex justify-center border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
disabled={isLoading || !stripe || !elements}
|
||||
id="submit"
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
'Pay $' + totalAmount
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Show any error or success messages */}
|
||||
{message && <div className="text-red-500" id="payment-message">{message}</div>}
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ClubPayment() {
|
||||
const [clientSecret, setClientSecret] = useState('');
|
||||
const [paymentIntent, setPaymentIntent] = useState('');
|
||||
useEffect(() => {
|
||||
// Create PaymentIntent as soon as the page loads using our local API
|
||||
fetch('api/stripe_intent', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
amount: 2000,
|
||||
payment_intent_id: '',
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setClientSecret(data.client_secret);
|
||||
setPaymentIntent(data.id);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const appearance: Appearance = {
|
||||
theme: 'stripe',
|
||||
labels: 'floating',
|
||||
};
|
||||
|
||||
const options: StripeElementsOptions = {
|
||||
clientSecret,
|
||||
appearance
|
||||
}
|
||||
return <>
|
||||
{clientSecret && (
|
||||
<Elements options={options}
|
||||
stripe={stripe}>
|
||||
<CheckoutForm paymentIntentID={paymentIntent} />
|
||||
</Elements>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -2,6 +2,18 @@ import assert from 'assert';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { Feed } from 'feed';
|
||||
|
||||
interface Author {
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
const authors: Record<string, Author> = {
|
||||
'Tony Grosinger': {
|
||||
name: 'Tony Grosinger',
|
||||
email: 'tony@grosinger.net',
|
||||
},
|
||||
};
|
||||
|
||||
export async function GET(req: Request) {
|
||||
let siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
|
||||
|
||||
@ -9,15 +21,10 @@ export async function GET(req: Request) {
|
||||
throw Error('Missing NEXT_PUBLIC_SITE_URL environment variable');
|
||||
}
|
||||
|
||||
let author = {
|
||||
name: 'Spencer Sharp',
|
||||
email: 'spencer@planetaria.tech',
|
||||
};
|
||||
|
||||
let feed = new Feed({
|
||||
title: author.name,
|
||||
description: 'Your blog description',
|
||||
author,
|
||||
title: 'West Sound Community Hall',
|
||||
description:
|
||||
'History, Announcements, and more from the West Sound Hall and Community Club.',
|
||||
id: siteUrl,
|
||||
link: siteUrl,
|
||||
image: `${siteUrl}/favicon.ico`,
|
||||
@ -43,6 +50,7 @@ export async function GET(req: Request) {
|
||||
let article = $('article').first();
|
||||
let title = article.find('h1').first().text();
|
||||
let date = article.find('time').first().attr('datetime');
|
||||
let author = article.find('#byline').first().text().slice(3); // Remove "by " from beginning
|
||||
let content = article.find('[data-mdx-content]').first().html();
|
||||
|
||||
assert(typeof title === 'string');
|
||||
@ -54,8 +62,8 @@ export async function GET(req: Request) {
|
||||
id: publicUrl,
|
||||
link: publicUrl,
|
||||
content,
|
||||
author: [author],
|
||||
contributor: [author],
|
||||
author: [authors[author]],
|
||||
contributor: [authors[author]],
|
||||
date: new Date(date),
|
||||
});
|
||||
}
|
||||
|
@ -5,13 +5,8 @@ import clsx from 'clsx'
|
||||
import { Card } from '@/components/Card'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
GitHubIcon,
|
||||
InstagramIcon,
|
||||
LinkedInIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import portraitImage from '@/images/portrait.jpg'
|
||||
import originalDeedImage from '@/images/original-deed.png'
|
||||
import { EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
@ -37,17 +32,6 @@ function SocialLink({
|
||||
)
|
||||
}
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M6 5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V8a3 3 0 0 0-3-3H6Zm.245 2.187a.75.75 0 0 0-.99 1.126l6.25 5.5a.75.75 0 0 0 .99 0l6.25-5.5a.75.75 0 0 0-.99-1.126L12 12.251 6.245 7.187Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function TimelineEntry({
|
||||
title,
|
||||
description,
|
||||
@ -107,11 +91,14 @@ export default function About() {
|
||||
<div className="lg:pl-20">
|
||||
<div className="max-w-xs px-2.5 lg:max-w-none">
|
||||
<Image
|
||||
src={portraitImage}
|
||||
src={originalDeedImage}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="aspect-square rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
className="rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
/>
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500">
|
||||
Original property deed from Alexander Chalmers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
@ -274,22 +261,9 @@ export default function About() {
|
||||
</div>
|
||||
<div className="lg:pl-20">
|
||||
<ul role="list">
|
||||
<SocialLink href="#" icon={TwitterIcon}>
|
||||
Follow on Twitter
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={InstagramIcon} className="mt-4">
|
||||
Follow on Instagram
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={GitHubIcon} className="mt-4">
|
||||
Follow on GitHub
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={LinkedInIcon} className="mt-4">
|
||||
Follow on LinkedIn
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={MailIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
icon={EnvelopeIcon}
|
||||
>
|
||||
contact@westsoundhall.org
|
||||
</SocialLink>
|
||||
|
@ -7,12 +7,12 @@ import '@/styles/tailwind.css'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s - Spencer Sharp',
|
||||
template: '%s - West Sound Hall',
|
||||
default:
|
||||
'Spencer Sharp - Software designer, founder, and amateur astronaut',
|
||||
'West Sound Hall - A community space on Orcas Island',
|
||||
},
|
||||
description:
|
||||
'I’m Spencer, a software designer and entrepreneur based in New York City. I’m the founder and CEO of Planetaria, where we develop technologies that empower regular people to explore space on their own terms.',
|
||||
'West Sound Community Hall, located at 884 Deer Harbor Road in the hamlet of West Sound, has served as a public assembly hall since it was built by volunteers in 1902.',
|
||||
alternates: {
|
||||
types: {
|
||||
'application/rss+xml': `${process.env.NEXT_PUBLIC_SITE_URL}/feed.xml`,
|
||||
|
238
src/app/page.tsx
@ -1,87 +1,18 @@
|
||||
import Image, { type ImageProps } from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Card } from '@/components/Card'
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
GitHubIcon,
|
||||
InstagramIcon,
|
||||
LinkedInIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import logoAirbnb from '@/images/logos/airbnb.svg'
|
||||
import logoFacebook from '@/images/logos/facebook.svg'
|
||||
import logoPlanetaria from '@/images/logos/planetaria.svg'
|
||||
import logoStarbucks from '@/images/logos/starbucks.svg'
|
||||
import image1 from '@/images/photos/image-1.jpg'
|
||||
import image2 from '@/images/photos/image-2.jpg'
|
||||
import image3 from '@/images/photos/image-3.jpg'
|
||||
import image4 from '@/images/photos/image-4.jpg'
|
||||
import image5 from '@/images/photos/image-5.jpg'
|
||||
import { CalendarDaysIcon, EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
import exteriorFrontImage from '@/images/photos/exterior-front.png'
|
||||
import stageImage from '@/images/photos/stage.jpg'
|
||||
import exteriorSoutheastImage from '@/images/photos/exterior-southeast.jpg'
|
||||
import interorEmptyImage from '@/images/photos/interior-empty.jpg'
|
||||
import kitchenImage from '@/images/photos/kitchen.jpg'
|
||||
import { type BlogPostWithSlug, getAllBlogPosts } from '@/lib/articles'
|
||||
import { formatDate } from '@/lib/formatDate'
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M2.75 7.75a3 3 0 0 1 3-3h12.5a3 3 0 0 1 3 3v8.5a3 3 0 0 1-3 3H5.75a3 3 0 0 1-3-3v-8.5Z"
|
||||
className="fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500"
|
||||
/>
|
||||
<path
|
||||
d="m4 6 6.024 5.479a2.915 2.915 0 0 0 3.952 0L20 6"
|
||||
className="stroke-zinc-400 dark:stroke-zinc-500"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function BriefcaseIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M2.75 9.75a3 3 0 0 1 3-3h12.5a3 3 0 0 1 3 3v8.5a3 3 0 0 1-3 3H5.75a3 3 0 0 1-3-3v-8.5Z"
|
||||
className="fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500"
|
||||
/>
|
||||
<path
|
||||
d="M3 14.25h6.249c.484 0 .952-.002 1.316.319l.777.682a.996.996 0 0 0 1.316 0l.777-.682c.364-.32.832-.319 1.316-.319H21M8.75 6.5V4.75a2 2 0 0 1 2-2h2.5a2 2 0 0 1 2 2V6.5"
|
||||
className="stroke-zinc-400 dark:stroke-zinc-500"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ArrowDownIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M4.75 8.75 8 12.25m0 0 3.25-3.5M8 12.25v-8.5"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
@ -97,19 +28,6 @@ function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
)
|
||||
}
|
||||
|
||||
function SocialLink({
|
||||
icon: Icon,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Link> & {
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
}) {
|
||||
return (
|
||||
<Link className="group -m-1 p-1" {...props}>
|
||||
<Icon className="h-6 w-6 fill-zinc-500 transition group-hover:fill-zinc-600 dark:fill-zinc-400 dark:group-hover:fill-zinc-300" />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function Newsletter() {
|
||||
return (
|
||||
<form
|
||||
@ -117,11 +35,11 @@ function Newsletter() {
|
||||
className="rounded-2xl border border-zinc-100 p-6 dark:border-zinc-700/40"
|
||||
>
|
||||
<h2 className="flex text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<MailIcon className="h-6 w-6 flex-none" />
|
||||
<EnvelopeIcon className="h-6 w-6 flex-none fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500" />
|
||||
<span className="ml-3">Stay up to date</span>
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Get notified when I publish something new, and unsubscribe at any time.
|
||||
Get notified about upcoming events and stay in touch with the community.
|
||||
</p>
|
||||
<div className="mt-6 flex">
|
||||
<input
|
||||
@ -139,100 +57,77 @@ function Newsletter() {
|
||||
)
|
||||
}
|
||||
|
||||
interface Role {
|
||||
company: string
|
||||
interface Meeting {
|
||||
title: string
|
||||
logo: ImageProps['src']
|
||||
start: string | { label: string; dateTime: string }
|
||||
end: string | { label: string; dateTime: string }
|
||||
date: string
|
||||
startTime: string
|
||||
endTime?: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
function Role({ role }: { role: Role }) {
|
||||
let startLabel =
|
||||
typeof role.start === 'string' ? role.start : role.start.label
|
||||
let startDate =
|
||||
typeof role.start === 'string' ? role.start : role.start.dateTime
|
||||
|
||||
let endLabel = typeof role.end === 'string' ? role.end : role.end.label
|
||||
let endDate = typeof role.end === 'string' ? role.end : role.end.dateTime
|
||||
|
||||
function MeetingListItem({ meeting }: { meeting: Meeting }) {
|
||||
return (
|
||||
<li className="flex gap-4">
|
||||
<div className="relative mt-1 flex h-10 w-10 flex-none items-center justify-center rounded-full shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0">
|
||||
<Image src={role.logo} alt="" className="h-7 w-7" unoptimized />
|
||||
</div>
|
||||
<dl className="flex flex-auto flex-wrap gap-x-2">
|
||||
<dt className="sr-only">Company</dt>
|
||||
<dt className="sr-only">Title</dt>
|
||||
<dd className="w-full flex-none text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{role.company}
|
||||
</dd>
|
||||
<dt className="sr-only">Role</dt>
|
||||
<dd className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{role.title}
|
||||
{meeting.title}
|
||||
</dd>
|
||||
<dt className="sr-only">Date</dt>
|
||||
<dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${startLabel} until ${endLabel}`}
|
||||
>
|
||||
<time dateTime={startDate}>{startLabel}</time>{' '}
|
||||
<span aria-hidden="true">—</span>{' '}
|
||||
<time dateTime={endDate}>{endLabel}</time>
|
||||
<dd className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{meeting.date}
|
||||
</dd>
|
||||
<dt className="sr-only">Time</dt>
|
||||
{meeting.endTime
|
||||
? <dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${meeting.startTime} until ${meeting.endTime}`}
|
||||
>
|
||||
<time dateTime={meeting.date + ' ' + meeting.startTime}>{meeting.startTime}</time>{' '}
|
||||
<span aria-hidden="true">—</span>{' '}
|
||||
<time dateTime={meeting.date + ' ' + meeting.endTime}>{meeting.endTime}</time>{' '}
|
||||
</dd>
|
||||
: <dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${meeting.startTime}`}
|
||||
>
|
||||
<time dateTime={meeting.date + ' ' + meeting.startTime}>{meeting.startTime}</time>{' '}
|
||||
</dd>
|
||||
}
|
||||
</dl>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function Resume() {
|
||||
let resume: Array<Role> = [
|
||||
function Events() {
|
||||
let events: Array<Meeting> = [
|
||||
{
|
||||
company: 'Planetaria',
|
||||
title: 'CEO',
|
||||
logo: logoPlanetaria,
|
||||
start: '2019',
|
||||
end: {
|
||||
label: 'Present',
|
||||
dateTime: new Date().getFullYear().toString(),
|
||||
},
|
||||
title: 'Christmas Potluck',
|
||||
date: '2023-12-16',
|
||||
startTime: '6:00pm',
|
||||
},
|
||||
{
|
||||
company: 'Airbnb',
|
||||
title: 'Product Designer',
|
||||
logo: logoAirbnb,
|
||||
start: '2014',
|
||||
end: '2019',
|
||||
},
|
||||
{
|
||||
company: 'Facebook',
|
||||
title: 'iOS Software Engineer',
|
||||
logo: logoFacebook,
|
||||
start: '2011',
|
||||
end: '2014',
|
||||
},
|
||||
{
|
||||
company: 'Starbucks',
|
||||
title: 'Shift Supervisor',
|
||||
logo: logoStarbucks,
|
||||
start: '2008',
|
||||
end: '2011',
|
||||
title: 'Member Meeting',
|
||||
date: '2023-12-20',
|
||||
startTime: '6:00pm',
|
||||
endTime: '8:00pm',
|
||||
notes: 'Bring your own chair.'
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-zinc-100 p-6 dark:border-zinc-700/40">
|
||||
<h2 className="flex text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<BriefcaseIcon className="h-6 w-6 flex-none" />
|
||||
<span className="ml-3">Work</span>
|
||||
<CalendarDaysIcon className="h-6 w-6 flex-none fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500" />
|
||||
<span className="ml-3">Upcoming Events</span>
|
||||
</h2>
|
||||
<ol className="mt-6 space-y-4">
|
||||
{resume.map((role, roleIndex) => (
|
||||
<Role key={roleIndex} role={role} />
|
||||
{events.map((meeting, idx) => (
|
||||
<MeetingListItem key={idx} meeting={meeting} />
|
||||
))}
|
||||
</ol>
|
||||
<Button href="#" variant="secondary" className="group mt-6 w-full">
|
||||
Download CV
|
||||
<ArrowDownIcon className="h-4 w-4 stroke-zinc-400 transition group-active:stroke-zinc-600 dark:group-hover:stroke-zinc-50 dark:group-active:stroke-zinc-50" />
|
||||
Full Calendar
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
@ -244,7 +139,7 @@ function Photos() {
|
||||
return (
|
||||
<div className="mt-16 sm:mt-20">
|
||||
<div className="-my-4 flex justify-center gap-5 overflow-hidden py-4 sm:gap-8">
|
||||
{[image1, image2, image3, image4, image5].map((image, imageIndex) => (
|
||||
{[exteriorSoutheastImage, stageImage, exteriorFrontImage, interorEmptyImage, kitchenImage].map((image, imageIndex) => (
|
||||
<div
|
||||
key={image.src}
|
||||
className={clsx(
|
||||
@ -276,33 +171,14 @@ export default async function Home() {
|
||||
West Sound Community Hall
|
||||
</h1>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
WestSound Community Hall, located at 884 Deer Harbor Road in the hamlet of West Sound, has served as a public assembly hall since it was built by volunteers in 1902.
|
||||
The West Sound Community Hall is located in the hamlet of West Sound
|
||||
on Orcas Island, about 10 minutes from the ferry landing and
|
||||
Eastsound. It has served as a public assembly hall since it was
|
||||
built by volunteers in 1902.
|
||||
</p>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
Facing West Sound, the Hall is at the heart of the WestSound community.
|
||||
Facing West Sound, the Hall is at the heart of the West Sound community.
|
||||
</p>
|
||||
<div className="mt-6 flex gap-6">
|
||||
<SocialLink
|
||||
href="https://twitter.com"
|
||||
aria-label="Follow on Twitter"
|
||||
icon={TwitterIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://instagram.com"
|
||||
aria-label="Follow on Instagram"
|
||||
icon={InstagramIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://github.com"
|
||||
aria-label="Follow on GitHub"
|
||||
icon={GitHubIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://linkedin.com"
|
||||
aria-label="Follow on LinkedIn"
|
||||
icon={LinkedInIcon}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
<Photos />
|
||||
@ -315,7 +191,7 @@ export default async function Home() {
|
||||
</div>
|
||||
<div className="space-y-10 lg:pl-16 xl:pl-24">
|
||||
<Newsletter />
|
||||
<Resume />
|
||||
<Events />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
@ -4,13 +4,8 @@ import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
GitHubIcon,
|
||||
InstagramIcon,
|
||||
LinkedInIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import portraitImage from '@/images/portrait.jpg'
|
||||
import exteriorFront from '@/images/photos/exterior-front.png'
|
||||
import { EnvelopeIcon, PencilSquareIcon, QuestionMarkCircleIcon } from '@heroicons/react/24/solid'
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
@ -36,17 +31,6 @@ function SocialLink({
|
||||
)
|
||||
}
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M6 5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V8a3 3 0 0 0-3-3H6Zm.245 2.187a.75.75 0 0 0-.99 1.126l6.25 5.5a.75.75 0 0 0 .99 0l6.25-5.5a.75.75 0 0 0-.99-1.126L12 12.251 6.245 7.187Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'History',
|
||||
description:
|
||||
@ -60,10 +44,10 @@ export default function Rental() {
|
||||
<div className="lg:pl-20">
|
||||
<div className="max-w-xs px-2.5 lg:max-w-none">
|
||||
<Image
|
||||
src={portraitImage}
|
||||
src={exteriorFront}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="aspect-square rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
className="aspect-square rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -73,7 +57,7 @@ export default function Rental() {
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p>
|
||||
The WestSound Community Hall is a public assembly hall, which has
|
||||
The West Sound Community Hall is a public assembly hall, which has
|
||||
been in continuous operation since it was built in 1902. In 1999,
|
||||
the Hall was listed on the Washington Heritage Register. The West
|
||||
Sound Community Club, as steward of the Hall, makes the Hall
|
||||
@ -82,7 +66,7 @@ export default function Rental() {
|
||||
</p>
|
||||
<p>
|
||||
Rental and deposit fees must be paid prior to obtaining access for
|
||||
set-up. Rental fees include use of the meeting room, projector
|
||||
set-up. Rental fees include use of the meeting room, projector
|
||||
and stage, chairs and tables, kitchen, rest rooms, water, and
|
||||
lights. It does not include trash removal. Cleanup of the Hall
|
||||
including removal of trash from the property the same day as the
|
||||
@ -96,23 +80,184 @@ export default function Rental() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:pl-20">
|
||||
<div className="order-last sm:order-none lg:pl-20">
|
||||
<ul role="list">
|
||||
<SocialLink href="#" icon={TwitterIcon}>
|
||||
<SocialLink href="/WSCC-Hall-Rental-Application.pdf" icon={PencilSquareIcon}>
|
||||
Rental Application Form
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={InstagramIcon} className="mt-4">
|
||||
<SocialLink href='/WSCC-Hall-Rental-QA.pdf' icon={QuestionMarkCircleIcon} className="mt-4">
|
||||
Rental Q&A
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={MailIcon}
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
>
|
||||
contact@westsoundhall.org
|
||||
</SocialLink>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="lg:col-span-2">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-2xl">
|
||||
Rental Rates
|
||||
</h2>
|
||||
|
||||
<div className="overflow-x-auto -mx-4 sm:-mx-0">
|
||||
<div className="inline-block min-w-full py-2 align-middle">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0"></th>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Half day (4 hr)</th>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">All day</th>
|
||||
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Deposit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">Orcas non-profit organizations</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$50 or 2 hours for $30*</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$100</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">None†</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">WSCC and OIYC Members</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$50 or 2 hours for $30*</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$100</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">None†</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">Off-island non-profit organizations</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$50</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$100</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$300</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">Individuals and non-public use</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$40 per hour</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$300</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$300</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">Government sponsored activities</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$50</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">$50</td>
|
||||
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">None†</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-2xl">
|
||||
Use Restrictions
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
The West Sound Community Hall is a non-smoking facility.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The West Sound Community Club Board of Directors reserves the right
|
||||
to deny use of the West Sound Community Hall to any individual or
|
||||
organization without exception.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The West Sound Community Hall may not be used for either personal or
|
||||
organizational monetary gain or to promote business activities.
|
||||
The only exceptions to this restriction are:
|
||||
|
||||
<ol className="list-decimal ml-8 mt-5">
|
||||
<li>
|
||||
Use for fund raising activities sponsored by the West Sound
|
||||
Community Club, a nonprofit corporation, for purposes specified in
|
||||
Article II of the Bylaws of the West Sound Community Club.
|
||||
</li>
|
||||
<li>
|
||||
Use for fund raising activities sponsored by qualified nonprofit
|
||||
organizations with prior approval by the President of the West Sound
|
||||
Community Club. (Such activities must be less than five days in
|
||||
length and more than 50% of the proceeds must be distributed to the
|
||||
sponsoring nonprofit organization.)
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Any individual or organizational nonpublic use of the hall which
|
||||
requires payment of a fee by those attending or offers items for
|
||||
sale must be approved in advance by the Board of Directors of the
|
||||
West Sound Community Club.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If liquor is to be served, renter is responsible for obtaining the
|
||||
appropriate license from the
|
||||
<Link href="https://lcb.wa.gov/"
|
||||
target="_blank" className="pl-1 text-blue-600 visited:text-purple-600 hover:underline"
|
||||
>
|
||||
Washington State Liquor Control Board
|
||||
</Link>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Use of the Hall is subject to renter completing a Hall Rental
|
||||
Application which includes an agreement to abide by certain rules
|
||||
and restrictions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The Hall must be left clean and renter must remove trash from Hall
|
||||
property on the day of the event.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-2xl">
|
||||
Capacity
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
Maximum occupancy of the Hall is restricted to 126 people.
|
||||
Maximum table seating is 84 people.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-2xl">
|
||||
Parking
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
There is limited public parking available along both sides of Deer
|
||||
Harbor Road west of the Hall. However, additional parking for Hall
|
||||
events may be arranged on the grounds of the Orcas Island Yacht Club
|
||||
picnic shelter located east of the Hall. (Yacht Club events have
|
||||
priority on the use of this parking area.) When this parking area is
|
||||
available, use for most nonpublic events requires a $50 donation to
|
||||
the Yacht Club. Use of the Yacht Club parking area for activities
|
||||
other than parking, such as catering activities, bonfires and
|
||||
picnics is not allowed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Contact the Property Manager for details on parking.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-2xl">
|
||||
Accessibility
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
The Hall is accessible by wheelchair using a ramp on the west side.
|
||||
The bathrooms are not handicap accessible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
|
@ -1,98 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
|
||||
import { Card } from '@/components/Card'
|
||||
import { Section } from '@/components/Section'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
function SpeakingSection({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Section>) {
|
||||
return (
|
||||
<Section {...props}>
|
||||
<div className="space-y-16">{children}</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function Appearance({
|
||||
title,
|
||||
description,
|
||||
event,
|
||||
cta,
|
||||
href,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
event: string
|
||||
cta: string
|
||||
href: string
|
||||
}) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title as="h3" href={href}>
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow decorate>{event}</Card.Eyebrow>
|
||||
<Card.Description>{description}</Card.Description>
|
||||
<Card.Cta>{cta}</Card.Cta>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Speaking',
|
||||
description:
|
||||
'I’ve spoken at events all around the world and been interviewed for many podcasts.',
|
||||
}
|
||||
|
||||
export default function Speaking() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="I’ve spoken at events all around the world and been interviewed for many podcasts."
|
||||
intro="One of my favorite ways to share my ideas is live on stage, where there’s so much more communication bandwidth than there is in writing, and I love podcast interviews because they give me the opportunity to answer questions instead of just present my opinions."
|
||||
>
|
||||
<div className="space-y-20">
|
||||
<SpeakingSection title="Conferences">
|
||||
<Appearance
|
||||
href="#"
|
||||
title="In space, no one can watch you stream — until now"
|
||||
description="A technical deep-dive into HelioStream, the real-time streaming library I wrote for transmitting live video back to Earth."
|
||||
event="SysConf 2021"
|
||||
cta="Watch video"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Lessons learned from our first product recall"
|
||||
description="They say that if you’re not embarassed by your first version, you’re doing it wrong. Well when you’re selling DIY space shuttle kits it turns out it’s a bit more complicated."
|
||||
event="Business of Startups 2020"
|
||||
cta="Watch video"
|
||||
/>
|
||||
</SpeakingSection>
|
||||
<SpeakingSection title="Podcasts">
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Using design as a competitive advantage"
|
||||
description="How we used world-class visual design to attract a great team, win over customers, and get more press for Planetaria."
|
||||
event="Encoding Design, July 2022"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Bootstrapping an aerospace company to $17M ARR"
|
||||
description="The story of how we built one of the most promising space startups in the world without taking any capital from investors."
|
||||
event="The Escape Velocity Show, March 2022"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Programming your company operating system"
|
||||
description="On the importance of creating systems and processes for running your business so that everyone on the team knows how to make the right decision no matter the situation."
|
||||
event="How They Work Radio, September 2021"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
</SpeakingSection>
|
||||
</div>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
@ -3,15 +3,29 @@ import { type Metadata } from 'next'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'You’re subscribed',
|
||||
description: 'Thanks for subscribing to my newsletter.',
|
||||
title: 'Thank You',
|
||||
description: 'Thanks for becoming a member.',
|
||||
}
|
||||
|
||||
export default function ThankYou() {
|
||||
export default async function ThankYou({
|
||||
searchParams
|
||||
}: {
|
||||
searchParams: { [key: string]: string | string[] | undefined }
|
||||
}) {
|
||||
|
||||
const { redirect_status } = searchParams;
|
||||
if (redirect_status !== 'succeeded') {
|
||||
// TODO: Display error
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Thanks for subscribing."
|
||||
intro="I’ll send you an email any time I publish a new blog post, release a new project, or have anything interesting to share that I think you’d want to hear about. You can unsubscribe at any time, no hard feelings."
|
||||
/>
|
||||
title="Thanks for becoming a member."
|
||||
intro="Thank you for joining the West Sound Community Club."
|
||||
>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
Your financial support helps us perserve this historic building and to host events for the community. We'll add you to our member mailing list so you receive announcement emails about upcoming events.
|
||||
</p>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
import { Card } from '@/components/Card'
|
||||
import { Section } from '@/components/Section'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
function ToolsSection({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Section>) {
|
||||
return (
|
||||
<Section {...props}>
|
||||
<ul role="list" className="space-y-16">
|
||||
{children}
|
||||
</ul>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function Tool({
|
||||
title,
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
title: string
|
||||
href?: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Card as="li">
|
||||
<Card.Title as="h3" href={href}>
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Description>{children}</Card.Description>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'Uses',
|
||||
description: 'Software I use, gadgets I love, and other things I recommend.',
|
||||
}
|
||||
|
||||
export default function Uses() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Software I use, gadgets I love, and other things I recommend."
|
||||
intro="I get asked a lot about the things I use to build software, stay productive, or buy to fool myself into thinking I’m being productive when I’m really just procrastinating. Here’s a big list of all of my favorite stuff."
|
||||
>
|
||||
<div className="space-y-20">
|
||||
<ToolsSection title="Workstation">
|
||||
<Tool title="16” MacBook Pro, M1 Max, 64GB RAM (2021)">
|
||||
I was using an Intel-based 16” MacBook Pro prior to this and the
|
||||
difference is night and day. I’ve never heard the fans turn on a
|
||||
single time, even under the incredibly heavy loads I put it through
|
||||
with our various launch simulations.
|
||||
</Tool>
|
||||
<Tool title="Apple Pro Display XDR (Standard Glass)">
|
||||
The only display on the market if you want something HiDPI and
|
||||
bigger than 27”. When you’re working at planetary scale, every pixel
|
||||
you can get counts.
|
||||
</Tool>
|
||||
<Tool title="IBM Model M SSK Industrial Keyboard">
|
||||
They don’t make keyboards the way they used to. I buy these any time
|
||||
I see them go up for sale and keep them in storage in case I need
|
||||
parts or need to retire my main.
|
||||
</Tool>
|
||||
<Tool title="Apple Magic Trackpad">
|
||||
Something about all the gestures makes me feel like a wizard with
|
||||
special powers. I really like feeling like a wizard with special
|
||||
powers.
|
||||
</Tool>
|
||||
<Tool title="Herman Miller Aeron Chair">
|
||||
If I’m going to slouch in the worst ergonomic position imaginable
|
||||
all day, I might as well do it in an expensive chair.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Development tools">
|
||||
<Tool title="Sublime Text 4">
|
||||
I don’t care if it’s missing all of the fancy IDE features everyone
|
||||
else relies on, Sublime Text is still the best text editor ever
|
||||
made.
|
||||
</Tool>
|
||||
<Tool title="iTerm2">
|
||||
I’m honestly not even sure what features I get with this that aren’t
|
||||
just part of the macOS Terminal but it’s what I use.
|
||||
</Tool>
|
||||
<Tool title="TablePlus">
|
||||
Great software for working with databases. Has saved me from
|
||||
building about a thousand admin interfaces for my various projects
|
||||
over the years.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Design">
|
||||
<Tool title="Figma">
|
||||
We started using Figma as just a design tool but now it’s become our
|
||||
virtual whiteboard for the entire company. Never would have expected
|
||||
the collaboration features to be the real hook.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Productivity">
|
||||
<Tool title="Alfred">
|
||||
It’s not the newest kid on the block but it’s still the fastest. The
|
||||
Sublime Text of the application launcher world.
|
||||
</Tool>
|
||||
<Tool title="Reflect">
|
||||
Using a daily notes system instead of trying to keep things
|
||||
organized by topics has been super powerful for me. And with
|
||||
Reflect, it’s still easy for me to keep all of that stuff
|
||||
discoverable by topic even though all of my writing happens in the
|
||||
daily note.
|
||||
</Tool>
|
||||
<Tool title="SavvyCal">
|
||||
Great tool for scheduling meetings while protecting my calendar and
|
||||
making sure I still have lots of time for deep work during the week.
|
||||
</Tool>
|
||||
<Tool title="Focus">
|
||||
Simple tool for blocking distracting websites when I need to just do
|
||||
the work and get some momentum going.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
</div>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
@ -58,6 +58,7 @@ export function ArticleLayout({
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
<span className="ml-3">{formatDate(article.date)}</span>
|
||||
</time>
|
||||
<span id="byline" className='mt-4 text-base text-zinc-400 dark:text-zinc-500'>by {article.author}</span>
|
||||
</header>
|
||||
<Prose className="mt-8" data-mdx-content>
|
||||
{children}
|
||||
|
@ -32,8 +32,7 @@ export function Footer() {
|
||||
<NavLink href="/club">Club</NavLink>
|
||||
</div>
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500">
|
||||
© {new Date().getFullYear()} Spencer Sharp. All rights
|
||||
reserved.
|
||||
© {new Date().getFullYear()} West Sound Community Club. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</ContainerInner>
|
||||
|
@ -1,36 +0,0 @@
|
||||
export function TwitterIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path d="M20.055 7.983c.011.174.011.347.011.523 0 5.338-3.92 11.494-11.09 11.494v-.003A10.755 10.755 0 0 1 3 18.186c.308.038.618.057.928.058a7.655 7.655 0 0 0 4.841-1.733c-1.668-.032-3.13-1.16-3.642-2.805a3.753 3.753 0 0 0 1.76-.07C5.07 13.256 3.76 11.6 3.76 9.676v-.05a3.77 3.77 0 0 0 1.77.505C3.816 8.945 3.288 6.583 4.322 4.737c1.98 2.524 4.9 4.058 8.034 4.22a4.137 4.137 0 0 1 1.128-3.86A3.807 3.807 0 0 1 19 5.274a7.657 7.657 0 0 0 2.475-.98c-.29.934-.9 1.729-1.713 2.233A7.54 7.54 0 0 0 22 5.89a8.084 8.084 0 0 1-1.945 2.093Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function InstagramIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path d="M12 3c-2.444 0-2.75.01-3.71.054-.959.044-1.613.196-2.185.418A4.412 4.412 0 0 0 4.51 4.511c-.5.5-.809 1.002-1.039 1.594-.222.572-.374 1.226-.418 2.184C3.01 9.25 3 9.556 3 12s.01 2.75.054 3.71c.044.959.196 1.613.418 2.185.23.592.538 1.094 1.039 1.595.5.5 1.002.808 1.594 1.038.572.222 1.226.374 2.184.418C9.25 20.99 9.556 21 12 21s2.75-.01 3.71-.054c.959-.044 1.613-.196 2.185-.419a4.412 4.412 0 0 0 1.595-1.038c.5-.5.808-1.002 1.038-1.594.222-.572.374-1.226.418-2.184.044-.96.054-1.267.054-3.711s-.01-2.75-.054-3.71c-.044-.959-.196-1.613-.419-2.185A4.412 4.412 0 0 0 19.49 4.51c-.5-.5-1.002-.809-1.594-1.039-.572-.222-1.226-.374-2.184-.418C14.75 3.01 14.444 3 12 3Zm0 1.622c2.403 0 2.688.009 3.637.052.877.04 1.354.187 1.67.31.421.163.72.358 1.036.673.315.315.51.615.673 1.035.123.317.27.794.31 1.671.043.95.052 1.234.052 3.637s-.009 2.688-.052 3.637c-.04.877-.187 1.354-.31 1.67-.163.421-.358.72-.673 1.036a2.79 2.79 0 0 1-1.035.673c-.317.123-.794.27-1.671.31-.95.043-1.234.052-3.637.052s-2.688-.009-3.637-.052c-.877-.04-1.354-.187-1.67-.31a2.789 2.789 0 0 1-1.036-.673 2.79 2.79 0 0 1-.673-1.035c-.123-.317-.27-.794-.31-1.671-.043-.95-.052-1.234-.052-3.637s.009-2.688.052-3.637c.04-.877.187-1.354.31-1.67.163-.421.358-.72.673-1.036.315-.315.615-.51 1.035-.673.317-.123.794-.27 1.671-.31.95-.043 1.234-.052 3.637-.052Z" />
|
||||
<path d="M12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6Zm0-7.622a4.622 4.622 0 1 0 0 9.244 4.622 4.622 0 0 0 0-9.244Zm5.884-.182a1.08 1.08 0 1 1-2.16 0 1.08 1.08 0 0 1 2.16 0Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GitHubIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 2C6.475 2 2 6.588 2 12.253c0 4.537 2.862 8.369 6.838 9.727.5.09.687-.218.687-.487 0-.243-.013-1.05-.013-1.91C7 20.059 6.35 18.957 6.15 18.38c-.113-.295-.6-1.205-1.025-1.448-.35-.192-.85-.667-.013-.68.788-.012 1.35.744 1.538 1.051.9 1.551 2.338 1.116 2.912.846.088-.666.35-1.115.638-1.371-2.225-.256-4.55-1.14-4.55-5.062 0-1.115.387-2.038 1.025-2.756-.1-.256-.45-1.307.1-2.717 0 0 .837-.269 2.75 1.051.8-.23 1.65-.346 2.5-.346.85 0 1.7.115 2.5.346 1.912-1.333 2.75-1.05 2.75-1.05.55 1.409.2 2.46.1 2.716.637.718 1.025 1.628 1.025 2.756 0 3.934-2.337 4.806-4.562 5.062.362.32.675.936.675 1.897 0 1.371-.013 2.473-.013 2.82 0 .268.188.589.688.486a10.039 10.039 0 0 0 4.932-3.74A10.447 10.447 0 0 0 22 12.253C22 6.588 17.525 2 12 2Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function LinkedInIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path d="M18.335 18.339H15.67v-4.177c0-.996-.02-2.278-1.39-2.278-1.389 0-1.601 1.084-1.601 2.205v4.25h-2.666V9.75h2.56v1.17h.035c.358-.674 1.228-1.387 2.528-1.387 2.7 0 3.2 1.778 3.2 4.091v4.715zM7.003 8.575a1.546 1.546 0 01-1.548-1.549 1.548 1.548 0 111.547 1.549zm1.336 9.764H5.666V9.75H8.34v8.589zM19.67 3H4.329C3.593 3 3 3.58 3 4.297v15.406C3 20.42 3.594 21 4.328 21h15.338C20.4 21 21 20.42 21 19.703V4.297C21 3.58 20.4 3 19.666 3h.003z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
Before Width: | Height: | Size: 237 KiB |
@ -1,13 +0,0 @@
|
||||
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="28" height="28" rx="14" fill="#FF5A5F" />
|
||||
<g clip-path="url(#a)">
|
||||
<path
|
||||
d="M14.001 18.183c-.902-1.131-1.432-2.122-1.609-2.971-.175-.685-.106-1.232.194-1.643.318-.474.792-.704 1.414-.704.622 0 1.096.23 1.414.708.297.407.372.955.19 1.644-.194.865-.723 1.856-1.608 2.972l.005-.006Zm6.4.76c-.123.831-.689 1.52-1.466 1.856-1.502.653-2.989-.389-4.261-1.803 2.104-2.634 2.493-4.685 1.59-6.012-.53-.76-1.289-1.13-2.263-1.13-1.963 0-3.042 1.66-2.618 3.588.247 1.043.901 2.229 1.945 3.555-.654.723-1.274 1.237-1.822 1.555-.424.23-.83.372-1.218.406-1.786.266-3.186-1.467-2.55-3.253.088-.23.263-.654.563-1.308l.017-.035c.976-2.119 2.161-4.527 3.523-7.197l.035-.088.387-.744c.3-.548.423-.793.9-1.095.231-.14.514-.21.831-.21.636 0 1.132.372 1.344.671.106.16.23.372.388.636l.372.726.054.106c1.36 2.669 2.547 5.072 3.52 7.196l.016.016.356.814.212.509c.162.409.196.815.142 1.239l.004.002Zm.814-1.593c-.124-.389-.337-.847-.6-1.396v-.02a235.28 235.28 0 0 0-3.538-7.23l-.074-.108C16.212 6.974 15.646 6 14.001 6c-1.627 0-2.317 1.13-3.023 2.599l-.054.106a221.31 221.31 0 0 0-3.536 7.232v.035l-.372.813c-.14.336-.212.512-.23.565-.9 2.477.955 4.65 3.201 4.65.018 0 .088 0 .177-.018h.248c1.166-.142 2.37-.883 3.59-2.211 1.218 1.326 2.422 2.069 3.587 2.211h.248c.089.018.16.018.177.018 2.246.002 4.101-2.174 3.201-4.65Z"
|
||||
fill="#fff" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" transform="translate(6 6)" d="M0 0h16v16H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,18 +0,0 @@
|
||||
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="28" height="28" rx="14" fill="#fff" />
|
||||
<g clip-path="url(#b)">
|
||||
<path
|
||||
d="M29 14.09c0-8.283-6.716-15-15-15-8.284 0-15 6.717-15 15 0 7.488 5.485 13.693 12.656 14.818v-10.48H7.847V14.09h3.81v-3.303c0-3.759 2.24-5.836 5.666-5.836 1.64 0 3.357.294 3.357.294v3.69h-1.892c-1.864 0-2.445 1.157-2.445 2.343v2.813h4.16l-.666 4.337h-3.494V28.91C23.515 27.783 29 21.577 29 14.09Z"
|
||||
fill="#1877F2" />
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<rect width="28" height="28" rx="14" fill="#fff" />
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<path fill="#fff" transform="translate(-1 -1)" d="M0 0h30v30H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 781 B |
Before Width: | Height: | Size: 6.8 KiB |
BIN
src/images/original-deed.png
Normal file
After Width: | Height: | Size: 2.4 MiB |
BIN
src/images/photos/exterior-front.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
src/images/photos/exterior-southeast.jpg
Normal file
After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 2.4 MiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 1.7 MiB |
BIN
src/images/photos/interior-empty.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
src/images/photos/kitchen.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/images/photos/stage.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 286 KiB |
38
src/lib/email.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { ServerClient } from 'postmark';
|
||||
|
||||
const fromAddr = process.env.FROM_ADDRESS || '';
|
||||
const toAddr = process.env.ADMIN_ADDRESS || '';
|
||||
const serverToken = process.env.POSTMARK_SERVER_TOKEN || '';
|
||||
const client = new ServerClient(serverToken);
|
||||
|
||||
export function emailNotification({
|
||||
name,
|
||||
type,
|
||||
amount,
|
||||
email,
|
||||
phone,
|
||||
address,
|
||||
}: {
|
||||
name: string;
|
||||
type: string;
|
||||
amount: number;
|
||||
email: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
}) {
|
||||
client.sendEmail({
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
Subject: 'New WSCC Membership',
|
||||
TextBody: [
|
||||
'New WSCC member:',
|
||||
'',
|
||||
'Name: ' + name,
|
||||
'Type: ' + type,
|
||||
'Amount: ' + amount,
|
||||
'Email: ' + email,
|
||||
'Phone: ' + phone,
|
||||
'Address: ' + address,
|
||||
].join('\n'),
|
||||
});
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import typographyPlugin from '@tailwindcss/typography'
|
||||
import formsPlugin from '@tailwindcss/forms';
|
||||
import { type Config } from 'tailwindcss'
|
||||
|
||||
import typographyStyles from './typography'
|
||||
@ -6,7 +7,10 @@ import typographyStyles from './typography'
|
||||
export default {
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
darkMode: 'class',
|
||||
plugins: [typographyPlugin],
|
||||
plugins: [
|
||||
typographyPlugin,
|
||||
formsPlugin,
|
||||
],
|
||||
theme: {
|
||||
fontSize: {
|
||||
xs: ['0.8125rem', { lineHeight: '1.5rem' }],
|
||||
|