Compare commits
31 Commits
9287b4da1a
...
0.0.26
Author | SHA1 | Date | |
---|---|---|---|
4cf7878202 | |||
346df14a25 | |||
b88febfc64 | |||
a60cba2762 | |||
3481adfcab | |||
18365af5dc | |||
768fa39cf2 | |||
94e316daf6 | |||
76d3e0a64d | |||
75c750c493 | |||
823ac4f5c8 | |||
292cefb945 | |||
7f3499aba1 | |||
f62e434dff | |||
7190b306e7 | |||
d9225e36d7 | |||
c957b156d8 | |||
f936bae555 | |||
52ec6d7b81 | |||
49751bb433 | |||
18f711444f | |||
c263bc4573 | |||
c4b5d24186 | |||
b532de646d | |||
283b5d8a49 | |||
e666588cc3 | |||
d98da909ae | |||
c7d3f92558 | |||
591ccc361b | |||
d935608d0c | |||
0ed3a4df64 |
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@ -0,0 +1,8 @@
|
||||
# Don't copy node_modules
|
||||
node_modules
|
||||
|
||||
# Ignore common folders that we do not need
|
||||
.git
|
||||
.next
|
||||
.github
|
||||
.vscode
|
@ -1 +1,7 @@
|
||||
NEXT_PUBLIC_SITE_URL=https://example.com
|
||||
NEXT_PUBLIC_SITE_URL=https://westsoundhall.org
|
||||
STRIPE_SECRET_KEY=sk_XXXX
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_XXXX
|
||||
POSTMARK_SERVER_TOKEN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
FROM_ADDRESS=support@westsoundhall.org
|
||||
ADMIN_ADDRESS=board@westsoundhall.org
|
||||
CALENDAR_ADDR=https://calendar.google.com/calendar/ical/westsoundcommunityclub%40gmail.com/private-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/basic.ics
|
||||
|
32
.gitea/workflows/image-build.yaml
Normal file
32
.gitea/workflows/image-build.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
name: Build Production Image
|
||||
run-name: $GITHUB_REF_TYPE Production Build
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*.*.*'
|
||||
|
||||
jobs:
|
||||
Build Production Image:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: git.grosinger.net/tgrosinger/runner-image:0.0.3
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Gitea Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.grosinger.net
|
||||
username: tgrosinger
|
||||
password: ${{ secrets.CONTAINER_REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: git.grosinger.net/tgrosinger/west-sound-hall:${{ gitea.ref_name }}
|
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@ -0,0 +1,20 @@
|
||||
FROM docker.io/library/node:18-alpine AS build-env
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV PORT 3000
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Building app
|
||||
COPY package*.json ./
|
||||
|
||||
# Install node modules
|
||||
# Note: We also install dev deps as TypeScript may be needed
|
||||
RUN npm install
|
||||
|
||||
# Copy files. Use dockerignore to avoid copying node_modules
|
||||
COPY . .
|
||||
|
||||
# Run app command
|
||||
CMD ["/bin/ash", "/app/entrypoint.sh"]
|
57
README.md
57
README.md
@ -1,20 +1,44 @@
|
||||
# Spotlight
|
||||
# West Sound Hall Website
|
||||
|
||||
Spotlight is a [Tailwind UI](https://tailwindui.com) site template built using [Tailwind CSS](https://tailwindcss.com) and [Next.js](https://nextjs.org).
|
||||
This is the website for the West Sound Hall and Community Club on Orcas Island, WA.
|
||||
|
||||
## Getting started
|
||||
https://westsoundhall.org
|
||||
|
||||
To get started with this template, first install the npm dependencies:
|
||||
## Running
|
||||
|
||||
Pre-build containers are created whenever a version is tagged in this repository. Pull the [latest version](https://git.grosinger.net/tgrosinger/-/packages/container/west-sound-hall/) and run on a server with Docker available.
|
||||
|
||||
```sh
|
||||
docker run -p 3000:3000 git.grosinger.net/tgrosinger/west-sound-hall:0.0.14
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
### Events on the Homepage and the Calendar
|
||||
|
||||
The events on the calendar are loaded from the westsoundcommunityclub@gmail.com Google Calendar.
|
||||
|
||||
Please note that all events on the calendar will be displayed. If an event should not reveal the title to the public, add the word "Private" to the event description (not the title).
|
||||
|
||||
### News Posts
|
||||
|
||||
News posts are written in [`src/app/news`](https://git.grosinger.net/tgrosinger/west-sound-hall/src/branch/main/src/app/news). Each post requires a directory within this folder, and the directory title will become the last segment of the news post URL.
|
||||
|
||||
To create a new post, create a new directory in that folder, then within that folder create a `page.mdx`. Use an existing news post as a template by copying its `page.mdx` then modify the author, date, title, description, and the body of the post as needed.
|
||||
|
||||
Posts are written in [mdx](https://mdxjs.com/) however for most news posts you can just consider the body of the post to be [standard markdown](https://www.markdownguide.org/basic-syntax/).
|
||||
|
||||
Photos can be added in the same directory as the `page.mdx` file. Refer to another news post for an example of how to embed them.
|
||||
|
||||
## Developing
|
||||
|
||||
To get started, first install the npm dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Next, create a `.env.local` file in the root of your project and set the `NEXT_PUBLIC_SITE_URL` variable to your site's public URL:
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_SITE_URL=https://example.com
|
||||
```
|
||||
Next, copy the `.env.example` file from this directory and call it `.env.local`. Fill in the values that have been redacted with their actual secrets. Be sure to use the test environment key from Stripe unless you are setting up production.
|
||||
|
||||
Next, run the development server:
|
||||
|
||||
@ -24,19 +48,8 @@ npm run dev
|
||||
|
||||
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
|
||||
|
||||
## Customizing
|
||||
|
||||
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files.
|
||||
|
||||
## License
|
||||
|
||||
This site template is a commercial product and is licensed under the [Tailwind UI license](https://tailwindui.com/license).
|
||||
This site is based off of the Spotlight template from Tailwind, and licensed under the [Tailwind UI license](https://tailwindui.com/license).
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about the technologies used in this site template, see the following resources:
|
||||
|
||||
- [Tailwind CSS](https://tailwindcss.com/docs) - the official Tailwind CSS documentation
|
||||
- [Next.js](https://nextjs.org/docs) - the official Next.js documentation
|
||||
- [Headless UI](https://headlessui.dev) - the official Headless UI documentation
|
||||
- [MDX](https://mdxjs.com) - the MDX documentation
|
||||
It was purchased by [Tony Grosinger](mailto:tony@grosinger.net).
|
||||
|
7
entrypoint.sh
Executable file
7
entrypoint.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The build step hard-codes variables from the environment into HTML, so it must
|
||||
# be done with the correct .env.local for runtime.
|
||||
|
||||
./node_modules/next/dist/bin/next build
|
||||
./node_modules/next/dist/bin/next start
|
724
package-lock.json
generated
724
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -10,40 +10,42 @@
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "1.7.17",
|
||||
"@headlessui/react": "1.7.19",
|
||||
"@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",
|
||||
"@next/mdx": "14.2.2",
|
||||
"@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/ical": "^0.8.3",
|
||||
"@types/react": "18.2.18",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/webpack-env": "^1.18.1",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"clsx": "^1.2.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"fast-glob": "^3.2.11",
|
||||
"feed": "^4.2.2",
|
||||
"next": "13.4.16",
|
||||
"next-themes": "^0.2.1",
|
||||
"ical": "^0.8.0",
|
||||
"next": "14.2.2",
|
||||
"postmark": "4.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "0.32.6",
|
||||
"stripe": "14.9.0",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "5.1.6"
|
||||
"tailwindcss": "3.4.3",
|
||||
"typescript": "5.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.4.7",
|
||||
"eslint": "8.45.0",
|
||||
"eslint-config-next": "13.4.16",
|
||||
"prettier": "^3.0.1",
|
||||
"prettier-plugin-tailwindcss": "^0.5.2",
|
||||
"sharp": "^0.32.0"
|
||||
"prettier-plugin-tailwindcss": "^0.5.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
import Image from 'next/image'
|
||||
import designSystem from './planetaria-design-system.png'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-09-05',
|
||||
title: 'Crafting a design system for a multiplanetary future',
|
||||
description:
|
||||
'Most companies try to stay ahead of the curve when it comes to visual design, but for Planetaria we needed to create a brand that would still inspire us 100 years from now when humanity has spread across our entire solar system.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
Most companies try to stay ahead of the curve when it comes to visual design, but for Planetaria we needed to create a brand that would still inspire us 100 years from now when humanity has spread across our entire solar system.
|
||||
|
||||
<Image src={designSystem} alt="" />
|
||||
|
||||
I knew that to get it right I was going to have to replicate the viewing conditions of someone from the future, so I grabbed my space helmet from the closet, created a new Figma document, and got to work.
|
||||
|
||||
## Sermone fata
|
||||
|
||||
Lorem markdownum, bracchia in redibam! Terque unda puppi nec, linguae posterior
|
||||
in utraque respicere candidus Mimasque formae; quae conantem cervice. Parcite
|
||||
variatus, redolentia adeunt. Tyrioque dies, naufraga sua adit partibus celanda
|
||||
torquere temptata, erit maneat et ramos, [iam](#) ait dominari
|
||||
potitus! Tibi litora matremque fumantia condi radicibus opusque.
|
||||
|
||||
Deus feram verumque, fecit, ira tamen, terras per alienae victum. Mutantur
|
||||
levitate quas ubi arcum ripas oculos abest. Adest [commissaque
|
||||
victae](#) in gemitus nectareis ire diva
|
||||
dotibus ora, et findi huic invenit; fatis? Fractaque dare superinposita
|
||||
nimiumque simulatoremque sanguine, at voce aestibus diu! Quid veterum hausit tu
|
||||
nil utinam paternos ima, commentaque.
|
||||
|
||||
```c
|
||||
exbibyte_wins = gigahertz(3);
|
||||
grayscaleUtilityClient = control_uat;
|
||||
pcmciaHibernate = oop_virus_console(text_mountain);
|
||||
if (stateWaisFirewire >= -2) {
|
||||
jfs = 647065 / ldapVrml(tutorialRestore, 85);
|
||||
metal_runtime_parse = roomComputingResolution - toolbarUpload +
|
||||
ipx_nvram_open;
|
||||
} else {
|
||||
maximizeSidebar *= suffix_url(flatbed + 2, requirements_encoding_node +
|
||||
only_qbe_media, minicomputer);
|
||||
}
|
||||
```
|
||||
|
||||
Aere repetiti cognataque natus. Habebat vela solutis saepe munus nondum adhuc
|
||||
oscula nomina pignora corpus deserat.
|
||||
|
||||
## Lethaei Pindumve me quae dinumerat Pavor
|
||||
|
||||
Idem se saxa fata pollentibus geminos; quos pedibus. Est urnis Herses omnes nec
|
||||
divite: et ille illa furit sim verbis Cyllenius.
|
||||
|
||||
1. Captus inpleverunt collo
|
||||
2. Nec nam placebant
|
||||
3. Siquos vulgus
|
||||
4. Dictis carissime fugae
|
||||
5. A tacitos nulla viginti
|
||||
|
||||
Ungues fistula annoso, ille addit linoque motatque uberior verso
|
||||
[rubuerunt](#) confine desuetaque. _Sanguine_ anteit
|
||||
emerguntque expugnacior est pennas iniqui ecce **haeret** genus: peiora imagine
|
||||
fossas Cephisos formosa! Refugitque amata [refelli](#)
|
||||
supplex. Summa brevis vetuere tenebas, hostes vetantis, suppressit, arreptum
|
||||
regna. Postquam conpescit iuvenis habet corpus, et erratica, perdere, tot mota
|
||||
ars talis.
|
||||
|
||||
```c
|
||||
digital.webcam_dual_frequency = webmasterMms;
|
||||
if (5 + language_standalone_google) {
|
||||
cc_inbox_layout *= file_character;
|
||||
task += p;
|
||||
lockUnicode += enterprise_monochrome(tokenFunctionPersonal, keyVirtual,
|
||||
adf);
|
||||
}
|
||||
windows_binary_esports(87734, array(restoreRomTopology, adRaw(407314),
|
||||
dongleBashThumbnail), interpreter);
|
||||
```
|
||||
|
||||
Sit volat naturam; motu Cancri. Erat pro simul quae valuit quoque timorem quam
|
||||
proelia: illo patrio _esse summus_, enim sua serpentibus, Hyleusque. Est coniuge
|
||||
recuso; refert Coroniden ignotos manat, adfectu.
|
Binary file not shown.
Before Width: | Height: | Size: 51 KiB |
@ -1,106 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-09-02',
|
||||
title: 'Introducing Animaginary: High performance web animations',
|
||||
description:
|
||||
'When you’re building a website for a company as ambitious as Planetaria, you need to make an impression. I wanted people to visit our website and see animations that looked more realistic than reality itself.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
When you’re building a website for a company as ambitious as Planetaria, you need to make an impression. I wanted people to visit our website and see animations that looked more realistic than reality itself.
|
||||
|
||||
To make this possible, we needed to squeeze every drop of performance out of the browser possible. And so Animaginary was born.
|
||||
|
||||
```js
|
||||
import { animate } from '@planetaria/animaginary'
|
||||
|
||||
export function MyComponent({ open, children }) {
|
||||
return (
|
||||
<animate.div
|
||||
in={open}
|
||||
animateFrom="opacity-0 scale-95"
|
||||
animateTo="opacity-100 scale-100"
|
||||
duration={350}
|
||||
>
|
||||
{children}
|
||||
</animate.div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Animaginary is our new web animation library that redefines what you thought was possible on the web. Hand-written in optimized WASM, Animaginary can even animate the `height` property of an element at 60fps.
|
||||
|
||||
## Sermone fata
|
||||
|
||||
Lorem markdownum, bracchia in redibam! Terque unda puppi nec, linguae posterior
|
||||
in utraque respicere candidus Mimasque formae; quae conantem cervice. Parcite
|
||||
variatus, redolentia adeunt. Tyrioque dies, naufraga sua adit partibus celanda
|
||||
torquere temptata, erit maneat et ramos, [iam](#) ait dominari
|
||||
potitus! Tibi litora matremque fumantia condi radicibus opusque.
|
||||
|
||||
Deus feram verumque, fecit, ira tamen, terras per alienae victum. Mutantur
|
||||
levitate quas ubi arcum ripas oculos abest. Adest [commissaque
|
||||
victae](#) in gemitus nectareis ire diva
|
||||
dotibus ora, et findi huic invenit; fatis? Fractaque dare superinposita
|
||||
nimiumque simulatoremque sanguine, at voce aestibus diu! Quid veterum hausit tu
|
||||
nil utinam paternos ima, commentaque.
|
||||
|
||||
```c
|
||||
exbibyte_wins = gigahertz(3);
|
||||
grayscaleUtilityClient = control_uat;
|
||||
pcmciaHibernate = oop_virus_console(text_mountain);
|
||||
if (stateWaisFirewire >= -2) {
|
||||
jfs = 647065 / ldapVrml(tutorialRestore, 85);
|
||||
metal_runtime_parse = roomComputingResolution - toolbarUpload +
|
||||
ipx_nvram_open;
|
||||
} else {
|
||||
maximizeSidebar *= suffix_url(flatbed + 2, requirements_encoding_node +
|
||||
only_qbe_media, minicomputer);
|
||||
}
|
||||
```
|
||||
|
||||
Aere repetiti cognataque natus. Habebat vela solutis saepe munus nondum adhuc
|
||||
oscula nomina pignora corpus deserat.
|
||||
|
||||
## Lethaei Pindumve me quae dinumerat Pavor
|
||||
|
||||
Idem se saxa fata pollentibus geminos; quos pedibus. Est urnis Herses omnes nec
|
||||
divite: et ille illa furit sim verbis Cyllenius.
|
||||
|
||||
1. Captus inpleverunt collo
|
||||
2. Nec nam placebant
|
||||
3. Siquos vulgus
|
||||
4. Dictis carissime fugae
|
||||
5. A tacitos nulla viginti
|
||||
|
||||
Ungues fistula annoso, ille addit linoque motatque uberior verso
|
||||
[rubuerunt](#) confine desuetaque. _Sanguine_ anteit
|
||||
emerguntque expugnacior est pennas iniqui ecce **haeret** genus: peiora imagine
|
||||
fossas Cephisos formosa! Refugitque amata [refelli](#)
|
||||
supplex. Summa brevis vetuere tenebas, hostes vetantis, suppressit, arreptum
|
||||
regna. Postquam conpescit iuvenis habet corpus, et erratica, perdere, tot mota
|
||||
ars talis.
|
||||
|
||||
```c
|
||||
digital.webcam_dual_frequency = webmasterMms;
|
||||
if (5 + language_standalone_google) {
|
||||
cc_inbox_layout *= file_character;
|
||||
task += p;
|
||||
lockUnicode += enterprise_monochrome(tokenFunctionPersonal, keyVirtual,
|
||||
adf);
|
||||
}
|
||||
windows_binary_esports(87734, array(restoreRomTopology, adRaw(407314),
|
||||
dongleBashThumbnail), interpreter);
|
||||
```
|
||||
|
||||
Sit volat naturam; motu Cancri. Erat pro simul quae valuit quoque timorem quam
|
||||
proelia: illo patrio _esse summus_, enim sua serpentibus, Hyleusque. Est coniuge
|
||||
recuso; refert Coroniden ignotos manat, adfectu.
|
@ -1,101 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2022-07-14',
|
||||
title: 'Rewriting the cosmOS kernel in Rust',
|
||||
description:
|
||||
'When we released the first version of cosmOS last year, it was written in Go. Go is a wonderful programming language, but it’s been a while since I’ve seen an article on the front page of Hacker News about rewriting some important tool in Go and I see articles on there about rewriting things in Rust every single week.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
When we released the first version of cosmOS last year, it was written in Go. Go is a wonderful programming language with a lot of benefits, but it’s been a while since I’ve seen an article on the front page of Hacker News about rewriting some important tool in Go and I see articles on there about rewriting things in Rust every single week.
|
||||
|
||||
```rust
|
||||
use ferris_says::say;
|
||||
use std::io::{stdout, BufWriter};
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let message = String::from("Hello fellow hackers");
|
||||
let width = message.chars().count();
|
||||
|
||||
let mut writer = BufWriter::new(stdout.lock());
|
||||
say(message.as_bytes(), width, &mut writer).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
I derive a large amount of my self-worth from whether or not Hacker News is impressed with the work I'm doing, so when I realized this, I cancelled all of our existing projects and started migrating everything to Rust immediately.
|
||||
|
||||
## Sermone fata
|
||||
|
||||
Lorem markdownum, bracchia in redibam! Terque unda puppi nec, linguae posterior
|
||||
in utraque respicere candidus Mimasque formae; quae conantem cervice. Parcite
|
||||
variatus, redolentia adeunt. Tyrioque dies, naufraga sua adit partibus celanda
|
||||
torquere temptata, erit maneat et ramos, [iam](#) ait dominari
|
||||
potitus! Tibi litora matremque fumantia condi radicibus opusque.
|
||||
|
||||
Deus feram verumque, fecit, ira tamen, terras per alienae victum. Mutantur
|
||||
levitate quas ubi arcum ripas oculos abest. Adest [commissaque
|
||||
victae](#) in gemitus nectareis ire diva
|
||||
dotibus ora, et findi huic invenit; fatis? Fractaque dare superinposita
|
||||
nimiumque simulatoremque sanguine, at voce aestibus diu! Quid veterum hausit tu
|
||||
nil utinam paternos ima, commentaque.
|
||||
|
||||
```c
|
||||
exbibyte_wins = gigahertz(3);
|
||||
grayscaleUtilityClient = control_uat;
|
||||
pcmciaHibernate = oop_virus_console(text_mountain);
|
||||
if (stateWaisFirewire >= -2) {
|
||||
jfs = 647065 / ldapVrml(tutorialRestore, 85);
|
||||
metal_runtime_parse = roomComputingResolution - toolbarUpload +
|
||||
ipx_nvram_open;
|
||||
} else {
|
||||
maximizeSidebar *= suffix_url(flatbed + 2, requirements_encoding_node +
|
||||
only_qbe_media, minicomputer);
|
||||
}
|
||||
```
|
||||
|
||||
Aere repetiti cognataque natus. Habebat vela solutis saepe munus nondum adhuc
|
||||
oscula nomina pignora corpus deserat.
|
||||
|
||||
## Lethaei Pindumve me quae dinumerat Pavor
|
||||
|
||||
Idem se saxa fata pollentibus geminos; quos pedibus. Est urnis Herses omnes nec
|
||||
divite: et ille illa furit sim verbis Cyllenius.
|
||||
|
||||
1. Captus inpleverunt collo
|
||||
2. Nec nam placebant
|
||||
3. Siquos vulgus
|
||||
4. Dictis carissime fugae
|
||||
5. A tacitos nulla viginti
|
||||
|
||||
Ungues fistula annoso, ille addit linoque motatque uberior verso
|
||||
[rubuerunt](#) confine desuetaque. _Sanguine_ anteit
|
||||
emerguntque expugnacior est pennas iniqui ecce **haeret** genus: peiora imagine
|
||||
fossas Cephisos formosa! Refugitque amata [refelli](#)
|
||||
supplex. Summa brevis vetuere tenebas, hostes vetantis, suppressit, arreptum
|
||||
regna. Postquam conpescit iuvenis habet corpus, et erratica, perdere, tot mota
|
||||
ars talis.
|
||||
|
||||
```c
|
||||
digital.webcam_dual_frequency = webmasterMms;
|
||||
if (5 + language_standalone_google) {
|
||||
cc_inbox_layout *= file_character;
|
||||
task += p;
|
||||
lockUnicode += enterprise_monochrome(tokenFunctionPersonal, keyVirtual,
|
||||
adf);
|
||||
}
|
||||
windows_binary_esports(87734, array(restoreRomTopology, adRaw(407314),
|
||||
dongleBashThumbnail), interpreter);
|
||||
```
|
||||
|
||||
Sit volat naturam; motu Cancri. Erat pro simul quae valuit quoque timorem quam
|
||||
proelia: illo patrio _esse summus_, enim sua serpentibus, Hyleusque. Est coniuge
|
||||
recuso; refert Coroniden ignotos manat, adfectu.
|
124
src/app/board-of-directors/page.tsx
Normal file
124
src/app/board-of-directors/page.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
import { type Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import { UserPlusIcon, GiftIcon, EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
import { TableCell, TableHeading, TableLeftHeading } from '@/components/Table'
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
href,
|
||||
children,
|
||||
icon: Icon,
|
||||
}: {
|
||||
className?: string
|
||||
href: string
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<li className={clsx(className, 'flex')}>
|
||||
<Link
|
||||
href={href}
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500"
|
||||
>
|
||||
<Icon className="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-teal-500" />
|
||||
<span className="ml-4">{children}</span>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'West Sound Community Club - Board of Directory',
|
||||
description:
|
||||
'The West Sound Community Club on Orcas Island.',
|
||||
}
|
||||
|
||||
|
||||
export default function Club() {
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
<div className="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
The Board of Directors
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600">
|
||||
<p>
|
||||
Elections for the Board of Directors are held annually at the October member meeting and potluck.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are interested in being one the ballot at the upcoming
|
||||
election, please
|
||||
<a href="mailto:board@westsoundhall.org"
|
||||
className="pl-1 text-blue-600 visited:text-purple-600 hover:underline">
|
||||
contact the board
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<TableHeading>Name</TableHeading>
|
||||
<TableHeading>Position</TableHeading>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 ">
|
||||
<tr>
|
||||
<TableLeftHeading>Lisa Pedersen</TableLeftHeading>
|
||||
<TableCell>President</TableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableLeftHeading>Betsy Wareham</TableLeftHeading>
|
||||
<TableCell>Vice President</TableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableLeftHeading>Tony Grosinger</TableLeftHeading>
|
||||
<TableCell>Secretary</TableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableLeftHeading>Temporarily performed by Secretary</TableLeftHeading>
|
||||
<TableCell>Treasurer</TableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableLeftHeading>Mark Gasser</TableLeftHeading>
|
||||
<TableCell>Director</TableCell>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableLeftHeading>Leslie Brown</TableLeftHeading>
|
||||
<TableCell>Director</TableCell>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:pl-20">
|
||||
<ul role="list">
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-4 border-zinc-100"
|
||||
>
|
||||
contact@westsoundhall.org
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-4 border-zinc-100"
|
||||
>
|
||||
board@westsoundhall.org
|
||||
</SocialLink>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
239
src/app/calendar/calendar.tsx
Normal file
239
src/app/calendar/calendar.tsx
Normal file
@ -0,0 +1,239 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ClockIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import React from 'react';
|
||||
import { Calendar, Event } from './page';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface day {
|
||||
date: dayjs.Dayjs;
|
||||
isCurrentMonth: boolean;
|
||||
isToday: boolean;
|
||||
}
|
||||
|
||||
|
||||
function classNames(...classes: string[]): string {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
const getEvents = (cal: Calendar, d: dayjs.Dayjs): Event[] => {
|
||||
const year = cal[d.year()];
|
||||
if (!year) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const month = year[d.month()];
|
||||
if (!month) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return month[d.date()] || [];
|
||||
}
|
||||
|
||||
export const CalendarComponent: React.FC<{ calendar: Calendar }> = ({ calendar }) => {
|
||||
const [selectedDay, setSelectedDay] = React.useState(dayjs().startOf('day'));
|
||||
const [selectedMonth, setSelectedMonth] = React.useState(dayjs().startOf('month'));
|
||||
|
||||
const days = React.useMemo(() => {
|
||||
// Number of greyed days shown before the beginning of the current month.
|
||||
const renderedDaysBeforeMonth = selectedMonth.day();
|
||||
const firstDay = selectedMonth.subtract(renderedDaysBeforeMonth, 'day');
|
||||
const month = selectedMonth.month();
|
||||
const today = dayjs().startOf('day');
|
||||
|
||||
let current = firstDay;
|
||||
const days: day[] = [];
|
||||
|
||||
// Fill out the first week with days from the previous month.
|
||||
for (let i = 0; i < renderedDaysBeforeMonth; i++) {
|
||||
days.push({
|
||||
date: current,
|
||||
isCurrentMonth: false,
|
||||
isToday: current.isSame(today),
|
||||
});
|
||||
current = current.add(1, 'day');
|
||||
}
|
||||
|
||||
// Add the days for the selected month.
|
||||
while (current.month() === month) {
|
||||
days.push({
|
||||
date: current,
|
||||
isCurrentMonth: true,
|
||||
isToday: current.isSame(today),
|
||||
});
|
||||
current = current.add(1, 'day');
|
||||
}
|
||||
|
||||
// Finish out the week with days from the following month.
|
||||
while (current.day() > 0) {
|
||||
days.push({
|
||||
date: current,
|
||||
isCurrentMonth: false,
|
||||
isToday: current.isSame(today),
|
||||
});
|
||||
current = current.add(1, 'day');
|
||||
}
|
||||
|
||||
return days;
|
||||
}, [selectedMonth]);
|
||||
|
||||
const isSelectedDay = (d: day): boolean => {
|
||||
return d.date.isSame(selectedDay);
|
||||
}
|
||||
|
||||
const selectedDayEvents = React.useMemo(() => {
|
||||
return getEvents(calendar, selectedDay);
|
||||
}, [calendar, selectedDay]);
|
||||
|
||||
return (
|
||||
<div className="lg:flex lg:h-full lg:flex-col">
|
||||
<header className="flex items-center justify-between border-b border-zinc-100 py-4 lg:flex-none">
|
||||
<h1 className="text-base font-semibold leading-6 text-zinc-800">
|
||||
<time dateTime={selectedMonth.format('YYYY-MM')}>{selectedMonth.format('MMMM YYYY')}</time>
|
||||
</h1>
|
||||
<div className="flex items-center">
|
||||
<div className="relative flex items-center rounded-md shadow-sm md:items-stretch">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setSelectedMonth(selectedMonth.subtract(1, 'month')) }}
|
||||
className="flex h-9 w-12 items-center justify-center rounded-l-md border-y border-l border-zinc-100 pr-1 text-gray-400 hover:text-gray-500 focus:relative md:w-9 md:pr-0 md:hover:bg-gray-50"
|
||||
>
|
||||
<span className="sr-only">Previous month</span>
|
||||
<ChevronLeftIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedDay(dayjs().startOf('day'))
|
||||
setSelectedMonth(dayjs().startOf('month'));
|
||||
}}
|
||||
className="hidden border-y border-zinc-100 px-3.5 text-sm font-semibold text-zinc-800 hover:bg-gray-50 focus:relative md:block"
|
||||
>
|
||||
Today
|
||||
</button>
|
||||
<span className="relative -mx-px h-5 w-px bg-gray-300 md:hidden" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setSelectedMonth(selectedMonth.add(1, 'month')) }}
|
||||
className="flex h-9 w-12 items-center justify-center rounded-r-md border-y border-r border-zinc-100 pl-1 text-gray-400 hover:text-gray-500 focus:relative md:w-9 md:pl-0 md:hover:bg-gray-50"
|
||||
>
|
||||
<span className="sr-only">Next month</span>
|
||||
<ChevronRightIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="shadow ring-1 ring-black ring-opacity-5 lg:flex lg:flex-auto lg:flex-col">
|
||||
<div className="grid grid-cols-7 gap-px border-b border-zinc-100 bg-gray-200 text-center text-xs font-semibold leading-6 text-gray-700 lg:flex-none">
|
||||
{[['M', 'on'], ['T', 'ue'], ['W', 'ed'], ['T', 'hu'], ['F', 'ri'], ['S', 'at'], ['S', 'un']].map((d) => (
|
||||
<div key={d[0] + d[1]} className="bg-white py-2">
|
||||
{d[0]}<span className="sr-only sm:not-sr-only">{d[1]}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex bg-gray-200 text-xs leading-6 text-gray-700 lg:flex-auto">
|
||||
<div className="isolate grid w-full grid-cols-7 gap-px">
|
||||
{days.map((day) => {
|
||||
const events = getEvents(calendar, day.date);
|
||||
const isSelected = isSelectedDay(day);
|
||||
|
||||
return (
|
||||
<button
|
||||
key={day.date.format('YYYY-MM-DD')}
|
||||
type="button"
|
||||
onClick={() => { setSelectedDay(day.date) }}
|
||||
className={classNames(
|
||||
day.isCurrentMonth ? 'bg-white' : 'bg-gray-50',
|
||||
isSelected || day.isToday ? 'font-semibold' : '',
|
||||
isSelected ? 'text-white' : '',
|
||||
!isSelected && day.isToday ? 'text-indigo-600' : '',
|
||||
!isSelected && day.isCurrentMonth && !day.isToday ? 'text-gray-900' : '',
|
||||
!isSelected && !day.isCurrentMonth && !day.isToday ? 'text-gray-500' : '',
|
||||
'flex h-12 sm:h-14 md:h-16 lg:h-24 flex-col px-3 py-2 hover:bg-gray-100 focus:z-10'
|
||||
)}
|
||||
>
|
||||
<time
|
||||
dateTime={day.date.format('YYYY-MM-DD')}
|
||||
className={classNames(
|
||||
isSelected ? 'flex h-6 w-6 items-center justify-center rounded-full' : '',
|
||||
isSelected && day.isToday ? 'bg-indigo-600' : '',
|
||||
isSelected && !day.isToday ? 'bg-gray-900' : '',
|
||||
'ml-auto'
|
||||
)}
|
||||
>
|
||||
{day.date.date()}
|
||||
</time>
|
||||
<span className="sr-only">{events.length} events</span>
|
||||
{events.length > 0 && (
|
||||
<>
|
||||
<span className="-mx-0.5 mt-auto flex flex-wrap-reverse lg:hidden">
|
||||
{events.map((event) => (
|
||||
<span key={event.id} className="mx-0.5 mb-1 h-1.5 w-1.5 rounded-full bg-gray-400" />
|
||||
))}
|
||||
</span>
|
||||
<ol className="mt-2 hidden lg:block">
|
||||
{events.slice(0, 2).map((event) => (
|
||||
<li key={event.id}>
|
||||
<p className="flex-auto truncate font-medium text-gray-900 group-hover:text-indigo-600">
|
||||
{event.name}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
{events.length > 2 && <li className="text-gray-500">+ {events.length - 2} more</li>}
|
||||
</ol>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-10">
|
||||
<h2 className="text-base font-semibold leading-6 text-zinc-800">
|
||||
Events on <time dateTime={selectedDay.format('YYYY-MM-DD')}>{selectedDay.format('MMMM D')}</time>
|
||||
</h2>
|
||||
{selectedDayEvents.length > 0 && (
|
||||
<div className="py-5">
|
||||
<ol className="divide-y divide-gray-100 overflow-hidden rounded-lg bg-white text-sm shadow ring-1 ring-black ring-opacity-5">
|
||||
{selectedDayEvents.map((event) => {
|
||||
const start = dayjs(event.start);
|
||||
return (
|
||||
<li key={event.id} className="group flex p-4 pr-6 focus-within:bg-gray-50 hover:bg-gray-50">
|
||||
<div className="flex-auto">
|
||||
<p className="font-semibold text-gray-900">{event.name}</p>
|
||||
<time dateTime={start.format('YYYY-MM-DD HH:mm')} className="mt-2 flex items-center text-gray-700">
|
||||
<ClockIcon className="mr-2 h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
{event.allDay
|
||||
? "All day"
|
||||
: start.format("h:mm a")
|
||||
}
|
||||
</time>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
)}
|
||||
{selectedDayEvents.length === 0 && (
|
||||
<p className="py-6 text-sm font-semibold text-gray-600">No events on this day</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
If you are interested in reserving the hall, please see the
|
||||
<Link href="/rental" className="pl-1 text-blue-600 hover:underline"
|
||||
>hall rental page
|
||||
</Link>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
126
src/app/calendar/page.tsx
Normal file
126
src/app/calendar/page.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import * as ical from "ical";
|
||||
import dayjs from "dayjs";
|
||||
import React from 'react';
|
||||
import { CalendarComponent } from "./calendar";
|
||||
import { Container } from "@/components/Container";
|
||||
import { Metadata } from "next";
|
||||
|
||||
const icalAddr = process.env.CALENDAR_ADDR || '';
|
||||
|
||||
export type Calendar = Record<number, MonthEvents>;
|
||||
type MonthEvents = Record<number, DayEvents>;
|
||||
type DayEvents = Record<number, Event[]>;
|
||||
|
||||
let calendar: Calendar;
|
||||
let upcomingEvents: Event[] = [];
|
||||
let calendarLastRefresh: dayjs.Dayjs;
|
||||
|
||||
export async function getCalendar(): Promise<Calendar> {
|
||||
const now = dayjs();
|
||||
if (!calendar || !calendarLastRefresh || now.diff(calendarLastRefresh, 'hours') >= 1) {
|
||||
await loadCalendar();
|
||||
calendarLastRefresh = now;
|
||||
}
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
export async function getUpcomingEvents(): Promise<Event[]> {
|
||||
await getCalendar();
|
||||
return upcomingEvents;
|
||||
}
|
||||
|
||||
/** Event must be a "plain object" so that it can be passed to the Calendar client component. */
|
||||
export interface Event {
|
||||
id: string;
|
||||
name: string;
|
||||
allDay: boolean;
|
||||
start: Date;
|
||||
end: Date;
|
||||
}
|
||||
|
||||
async function loadCalendar(): Promise<void> {
|
||||
if (icalAddr === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const icalContents = await (await fetch(icalAddr)).text();
|
||||
const events = ical.parseICS(icalContents);
|
||||
const thisMonth = dayjs().startOf('month');
|
||||
const yesterday = dayjs().startOf('day').subtract(1, 'day');
|
||||
const upcoming: Event[] = []
|
||||
|
||||
const cal: Calendar = {};
|
||||
for (const id in events) {
|
||||
if (!events.hasOwnProperty(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const event = events[id];
|
||||
const start = dayjs(event.start);
|
||||
const end = dayjs(event.end);
|
||||
const allDay = start.hour() === 0 && start.minute() === 0 && end.diff(start, 'hours') === 24;
|
||||
const privateEvent = event.description?.toLowerCase().includes("private");
|
||||
|
||||
const converted = {
|
||||
id: event.uid || "",
|
||||
name: privateEvent ? "Private event" : (event.summary || ""),
|
||||
allDay: allDay,
|
||||
start: start.toDate(),
|
||||
end: end.toDate(),
|
||||
}
|
||||
|
||||
// Don't spend any more time on events that aren't going to be displayed.
|
||||
if (start.isBefore(thisMonth)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (upcoming.length < 4 && start.isAfter(yesterday) && !privateEvent) {
|
||||
upcoming.push(converted);
|
||||
}
|
||||
|
||||
const year = start.year();
|
||||
const month = start.month();
|
||||
const day = start.date();
|
||||
|
||||
if (!(year in cal)) {
|
||||
cal[year] = {};
|
||||
}
|
||||
const yearMap = cal[year];
|
||||
|
||||
if (!(month in yearMap)) {
|
||||
yearMap[month] = {};
|
||||
}
|
||||
const monthMap = yearMap[month];
|
||||
|
||||
if (day in monthMap) {
|
||||
monthMap[day].push(converted);
|
||||
} else {
|
||||
monthMap[day] = [converted];
|
||||
}
|
||||
}
|
||||
|
||||
calendar = cal;
|
||||
upcomingEvents = upcoming;
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Events Calendar',
|
||||
description:
|
||||
'Upcoming events and rentals ast the West Sound Hall.',
|
||||
}
|
||||
|
||||
export default async function Calendar() {
|
||||
const calendar = await getCalendar();
|
||||
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
<header className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
West Sound Hall Calendar
|
||||
</h1>
|
||||
</header>
|
||||
<CalendarComponent calendar={calendar} />
|
||||
</Container>
|
||||
);
|
||||
}
|
@ -2,12 +2,12 @@
|
||||
import { type Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import dynamic from "next/dynamic";
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import { UserPlusIcon, GiftIcon, EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
import { UserPlusIcon, AtSymbolIcon, EnvelopeIcon, UserGroupIcon } from '@heroicons/react/24/solid'
|
||||
import interiorEmptyImage from '@/images/photos/interior-empty.jpg'
|
||||
import { ClubPayment } from './payment';
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
@ -24,7 +24,7 @@ function SocialLink({
|
||||
<li className={clsx(className, 'flex')}>
|
||||
<Link
|
||||
href={href}
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500 dark:text-zinc-200 dark:hover:text-teal-500"
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500"
|
||||
>
|
||||
<Icon className="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-teal-500" />
|
||||
<span className="ml-4">{children}</span>
|
||||
@ -34,7 +34,7 @@ function SocialLink({
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'History',
|
||||
title: 'Community Club',
|
||||
description:
|
||||
'The West Sound Community Club on Orcas Island.',
|
||||
}
|
||||
@ -43,6 +43,15 @@ export const metadata: Metadata = {
|
||||
// TODO: Replace interiorEmptyImage with a photo from a potluck
|
||||
|
||||
export default function Club() {
|
||||
|
||||
// Dynamic import since ClubPayment uses `document`
|
||||
const ClubPayment = dynamic(
|
||||
() => {
|
||||
return import("./payment");
|
||||
},
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
<div className="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
|
||||
@ -52,15 +61,15 @@ export default function Club() {
|
||||
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"
|
||||
className="aspect-square rounded-2xl bg-zinc-100 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
West Sound Community Club
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600">
|
||||
<p>
|
||||
The West Sound Community Club is a group of neighbors and friends
|
||||
living in the West Sound area. Together we steward the West Sound
|
||||
@ -88,19 +97,28 @@ export default function Club() {
|
||||
<SocialLink href="/WSCC-Membership-Form.pdf" icon={UserPlusIcon}>
|
||||
Membership Form
|
||||
</SocialLink>
|
||||
<SocialLink href="#" icon={GiftIcon} className="mt-4">
|
||||
Donations (Coming soon)
|
||||
<SocialLink href="https://groups.orcashub.org/invites/YkP79vjDtz" icon={AtSymbolIcon} className="mt-4">
|
||||
Club Member Mailing List
|
||||
</SocialLink>
|
||||
<SocialLink href="https://groups.orcashub.org/invites/Lg87tP3G2V" icon={AtSymbolIcon} className="mt-4">
|
||||
West Sound Neighbor to Neighbor Mailing List
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
className="mt-8 border-t border-zinc-100 pt-8"
|
||||
>
|
||||
contact@westsoundhall.org
|
||||
</SocialLink>
|
||||
<SocialLink href="/board-of-directors" icon={UserGroupIcon} className="mt-4">
|
||||
Board of Directors
|
||||
</SocialLink>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl mb-4 font-semibold leading-6 text-gray-900">
|
||||
Join or Renew your Membership
|
||||
</h2>
|
||||
<ClubPayment />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect, useState, FormEvent } from 'react';
|
||||
import React, { useEffect, useState, FormEvent, useCallback } from 'react';
|
||||
import { AddressElement, Elements } from '@stripe/react-stripe-js';
|
||||
import { Appearance, StripeAddressElementOptions, StripeElementsOptions, loadStripe } from '@stripe/stripe-js';
|
||||
import {
|
||||
@ -59,7 +59,6 @@ function CheckoutForm({
|
||||
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);
|
||||
@ -111,9 +110,7 @@ function CheckoutForm({
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetFees) {
|
||||
subtotal = Math.ceil(subtotal * 1.03)
|
||||
}
|
||||
|
||||
setTotalAmount(subtotal);
|
||||
|
||||
@ -128,7 +125,7 @@ function CheckoutForm({
|
||||
},
|
||||
}),
|
||||
});
|
||||
}, [paymentIntentID, selectedMembershipLevel, selectedAdditionalDonation, customAmount, offsetFees, email])
|
||||
}, [paymentIntentID, selectedMembershipLevel, selectedAdditionalDonation, customAmount, email])
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
@ -180,8 +177,8 @@ function CheckoutForm({
|
||||
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'
|
||||
active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-200',
|
||||
'relative flex cursor-pointer rounded-lg border bg-white hover:bg-gray-50 p-4 shadow-sm focus:outline-none'
|
||||
)
|
||||
}
|
||||
>
|
||||
@ -231,9 +228,9 @@ function CheckoutForm({
|
||||
'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'
|
||||
? 'ring-2 ring-indigo-600'
|
||||
: 'ring-1 ring-inset ring-gray-200 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 bg-white'
|
||||
)
|
||||
}
|
||||
>
|
||||
@ -253,7 +250,7 @@ function CheckoutForm({
|
||||
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"
|
||||
className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 ring-1 ring-inset ring-gray-200 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"
|
||||
/>
|
||||
@ -281,7 +278,7 @@ function CheckoutForm({
|
||||
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">
|
||||
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-200 focus-within:ring-2 focus-within:ring-indigo-600">
|
||||
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
|
||||
Email
|
||||
</label>
|
||||
@ -303,33 +300,16 @@ function CheckoutForm({
|
||||
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.
|
||||
Credit card fees included. If you would like to avoid these fees or
|
||||
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}
|
||||
@ -349,9 +329,36 @@ function CheckoutForm({
|
||||
);
|
||||
}
|
||||
|
||||
export function ClubPayment() {
|
||||
const DEFAULT_OPTIONS = {
|
||||
config: { attributes: true, childList: true, subtree: true },
|
||||
};
|
||||
|
||||
function useMutationObservable(targetEl: Node, cb: MutationCallback, options = DEFAULT_OPTIONS) {
|
||||
const [observer, setObserver] = useState<MutationObserver | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const obs = new MutationObserver(cb);
|
||||
setObserver(obs);
|
||||
}, [cb, options, setObserver]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!observer) return;
|
||||
const { config } = options;
|
||||
observer.observe(targetEl, config);
|
||||
return () => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
};
|
||||
}, [observer, targetEl, options]);
|
||||
}
|
||||
|
||||
export default function ClubPayment() {
|
||||
const [clientSecret, setClientSecret] = useState('');
|
||||
const [paymentIntent, setPaymentIntent] = useState('');
|
||||
|
||||
const htmlEl = document.getElementsByTagName('html')[0];
|
||||
|
||||
useEffect(() => {
|
||||
// Create PaymentIntent as soon as the page loads using our local API
|
||||
fetch('api/stripe_intent', {
|
||||
@ -369,15 +376,22 @@ export function ClubPayment() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const styles = getComputedStyle(htmlEl);
|
||||
|
||||
const appearance: Appearance = {
|
||||
theme: 'stripe',
|
||||
variables: {
|
||||
colorBackground: styles.getPropertyValue('--stripe-background'),
|
||||
colorText: styles.getPropertyValue('--stripe-foreground'),
|
||||
},
|
||||
labels: 'floating',
|
||||
};
|
||||
|
||||
const options: StripeElementsOptions = {
|
||||
clientSecret,
|
||||
appearance
|
||||
appearance,
|
||||
}
|
||||
|
||||
return <>
|
||||
{clientSecret && (
|
||||
<Elements options={options}
|
||||
|
@ -36,17 +36,17 @@ export async function GET(req: Request) {
|
||||
});
|
||||
|
||||
let articleIds = require
|
||||
.context('../blog', true, /\/page\.mdx$/)
|
||||
.context('../news', true, /\/page\.mdx$/)
|
||||
.keys()
|
||||
.filter((key) => key.startsWith('./'))
|
||||
.map((key) => key.slice(2).replace(/\/page\.mdx$/, ''));
|
||||
|
||||
for (let id of articleIds) {
|
||||
let url = String(new URL(`/blog/${id}`, req.url));
|
||||
let url = String(new URL(`/news/${id}`, req.url));
|
||||
let html = await (await fetch(url)).text();
|
||||
let $ = cheerio.load(html);
|
||||
|
||||
let publicUrl = `${siteUrl}/blog/${id}`;
|
||||
let publicUrl = `${siteUrl}/news/${id}`;
|
||||
let article = $('article').first();
|
||||
let title = article.find('h1').first().text();
|
||||
let date = article.find('time').first().attr('datetime');
|
||||
|
@ -23,7 +23,7 @@ function SocialLink({
|
||||
<li className={clsx(className, 'flex')}>
|
||||
<Link
|
||||
href={href}
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500 dark:text-zinc-200 dark:hover:text-teal-500"
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500"
|
||||
>
|
||||
<Icon className="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-teal-500" />
|
||||
<span className="ml-4">{children}</span>
|
||||
@ -79,7 +79,7 @@ function DateListItem({
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'History',
|
||||
title: 'Hall History',
|
||||
description:
|
||||
'The history of the West Sound Community Hall on Orcas Island.',
|
||||
}
|
||||
@ -94,18 +94,18 @@ export default function About() {
|
||||
src={originalDeedImage}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
className="rotate-3 rounded-2xl bg-zinc-100 object-cover"
|
||||
/>
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500">
|
||||
<p className="text-sm text-zinc-400">
|
||||
Original property deed from Alexander Chalmers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
Hall History
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600">
|
||||
<p>
|
||||
The West Sound Community Hall represents the history and character
|
||||
of Orcas Island. Members of the nonprofit West Sound Community Club,
|
||||
@ -130,7 +130,7 @@ export default function About() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 space-y-8">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
Detailed History
|
||||
</h2>
|
||||
|
||||
@ -154,7 +154,7 @@ export default function About() {
|
||||
title='1903 - 1935'
|
||||
description='During this period the following organizations were regular users of the Hall:'
|
||||
>
|
||||
<div className="relative z-10 ml-4 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<div className="relative z-10 ml-4 mt-2 text-sm text-zinc-600">
|
||||
<DateListItem
|
||||
year='1903'
|
||||
value='Women's Christian Temperance Union Woodmen Lodge'
|
||||
|
@ -27,7 +27,7 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="h-full antialiased" suppressHydrationWarning>
|
||||
<body className="flex h-full bg-zinc-50 dark:bg-black">
|
||||
<body className="flex h-full bg-zinc-50">
|
||||
<Providers>
|
||||
<div className="flex w-full">
|
||||
<Layout>{children}</Layout>
|
||||
|
BIN
src/app/news/2024-02-17-potluck/Boddingtons_Store_West_Sound.jpg
Normal file
BIN
src/app/news/2024-02-17-potluck/Boddingtons_Store_West_Sound.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 307 KiB |
BIN
src/app/news/2024-02-17-potluck/West_Sound.jpg
Normal file
BIN
src/app/news/2024-02-17-potluck/West_Sound.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 474 KiB |
BIN
src/app/news/2024-02-17-potluck/West_Sound_Main_Street.jpg
Normal file
BIN
src/app/news/2024-02-17-potluck/West_Sound_Main_Street.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 413 KiB |
58
src/app/news/2024-02-17-potluck/page.mdx
Normal file
58
src/app/news/2024-02-17-potluck/page.mdx
Normal file
@ -0,0 +1,58 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
import {MdxImage as Image } from '@/components/MdxImage'
|
||||
import boddingtonStore from './Boddingtons_Store_West_Sound.jpg'
|
||||
import westSoundMainStreet from './West_Sound_Main_Street.jpg'
|
||||
import westSound from './West_Sound.jpg'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2024-02-17',
|
||||
title: 'February 2024 Potluck',
|
||||
description: "The second potluck of the year, with a slidshow of Orcas history from John Wachter.",
|
||||
}
|
||||
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
This month after the potluck dinner, the community club was treated to a
|
||||
slideshow presentation from John Wachter. He showed us almost 100 photos from
|
||||
all over Orcas Island, many of which dated back to the early 1900's.
|
||||
|
||||
<div className="not-prose flex flex-col items-center">
|
||||
<Image src={boddingtonStore} alt="Boddington's Store at the end of Crow Valley Road" />
|
||||
<span className="mt-2 text-sm text-zinc-400">
|
||||
Boddington's Store at the end of Crow Valley Road.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Many of these images were available to us thanks to the [Orcas Island Historical
|
||||
Society](https://www.orcasmuseums.org/), and several of the photos were also
|
||||
passed down by John's family.
|
||||
|
||||
<div className="not-prose flex flex-col items-center">
|
||||
<Image src={westSoundMainStreet} alt="West Sound Main Street" />
|
||||
<span className="mt-2 text-sm text-zinc-400">
|
||||
West Sound Main Street
|
||||
</span>
|
||||
</div>
|
||||
|
||||
A recurring observation was both how many more buildings there were in West
|
||||
Sound, and how few trees there were on the island!
|
||||
|
||||
<div className="not-prose flex flex-col items-center">
|
||||
<Image src={westSound} alt="West Sound and the south end of Turtleback" />
|
||||
<span className="mt-2 text-sm text-zinc-400">
|
||||
West Sound and the south end of Turtleback
|
||||
</span>
|
||||
</div>
|
||||
|
||||
If you have ideas for presentations or activities at upcoming potlucks, please <a
|
||||
target="_blank" href="mailto:board@westsoundhall.org">send us a message</a>.
|
||||
|
||||
Thank you John, the Historical Society, and Peter Fisher for preserving and
|
||||
sharing this history.
|
132
src/app/news/2024-03-06-town-hall-meeting/page.mdx
Normal file
132
src/app/news/2024-03-06-town-hall-meeting/page.mdx
Normal file
@ -0,0 +1,132 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
import {MdxImage as Image } from '@/components/MdxImage'
|
||||
import townHallMeeting from './town-hall-meeting.jpg'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2024-03-06',
|
||||
title: 'March 2024 Town Hall',
|
||||
description: "A community gathering to brainstorm ideas for the club's future.",
|
||||
}
|
||||
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
On March 6th 2024 approximately 25 members of the club gathered in the hall to
|
||||
do some brainstorming. The goal was just to get ideas down on paper so that they
|
||||
can be prioritized and planned in the future.
|
||||
|
||||
<div className="not-prose flex flex-col items-center">
|
||||
<Image src={townHallMeeting} alt="Town Hall Meeting" />
|
||||
<span className="mt-2 text-sm text-zinc-400">
|
||||
Town Hall Meeting
|
||||
</span>
|
||||
</div>
|
||||
|
||||
The board of directors put together the list of categories and the community
|
||||
filled in the all the ideas.
|
||||
|
||||
Thank you to Mark Gasser and Joe Mentor for facilitating.
|
||||
|
||||
- Activities
|
||||
- Movie night
|
||||
- Ping pong
|
||||
- Use activities to attract people and build membership
|
||||
- Piggy-back on the OIYC pancake breakfast in May
|
||||
- Linkage with waterfront
|
||||
- Lack of parking – have to pay OIYC $50
|
||||
- Calendaring improvements – double booking with YC
|
||||
- BBQ (community outreach)
|
||||
- Olga days
|
||||
- Garage sale / rummage sale (Fidelis used to do this)
|
||||
- Deer Harbor had once mid-winter that was a huge success
|
||||
- Dance
|
||||
- Karaoke
|
||||
- End of season tool cleaning and sharpening
|
||||
- Jim Hamilton volunteered to bring his sharpener
|
||||
- Sip and paint
|
||||
- Guest lectures
|
||||
- Car & boat show
|
||||
- Cooking
|
||||
- Joint event with DHCC
|
||||
- Approved for-profit events.
|
||||
- We are in a smaller county so there are some exceptions.
|
||||
- Art classes, music classes, dance classes
|
||||
- A Saturday market might be allowed too.
|
||||
- Petitioning the county can be a strategy for adding more allowed activities.
|
||||
- Arthur mentioned that if you go through Parks and Recreation we might be able to do a dance.
|
||||
- Nancy had more information from her time as VP.
|
||||
- Building Improvements
|
||||
- A deck on the east side of the building extending to ground level on the north end.
|
||||
- Replace the kitchen.
|
||||
- A paint job in the kitchen would also go a long way.
|
||||
- Three compartment sink. Commercial dishwasher.
|
||||
- Joint project with the OIYC
|
||||
- Infrared heading panels or heat pump
|
||||
- Floor
|
||||
- Engineering study (or just an inspection)
|
||||
- Turn on the heat from your phone.
|
||||
- Replace the single pane windows.
|
||||
- Alternatively, adding shutters could protect the existing windows and improve their insulation.
|
||||
- Grant mentions that staying with in the purview of maintenance is easier to get through county approval rather than remodel.
|
||||
- New storage closet on the north end of the building.
|
||||
- Replace the hot water heater.
|
||||
- Restroom improvements.
|
||||
- Building Maintenance
|
||||
- Replacing siding on the north end of the building.
|
||||
- Address the leak between the kitchen and the north end of the building.
|
||||
- Use the compressor the blow out the water lines on cold days.
|
||||
- Funding
|
||||
- Could the historical society help us with fundraising since we’re a historic building?
|
||||
- Similarly, could the historical society host their meetings at our hall?
|
||||
- Grants
|
||||
- Bill Bangs
|
||||
- Need a needs analysis first
|
||||
- The county has a historic building preservation fund that can fund 50% of projects.
|
||||
- Convert Sail Orcas into a separate renter rather than being under the YC umbrella.
|
||||
- Advertise for rental of the hall. Chamber of commerce?
|
||||
- OICF bi-annual funding campaigns
|
||||
- Raise member dues
|
||||
- Sliding scale
|
||||
- Introductory rate, or the opposite - grandfather existing members at lower
|
||||
- Different funds that members can donate to
|
||||
- Membership capital campaign – direct asking
|
||||
- Increase membership
|
||||
- Make household double individual rate
|
||||
- Sell bricks
|
||||
- Governance
|
||||
- Build out more committees
|
||||
- Architectural
|
||||
- Colin Baden
|
||||
- Arthur
|
||||
- Membership / Welcoming
|
||||
- Encourage external groups to be committees, for example historical society or fidelis
|
||||
- Invite DHCC to one of our meetings
|
||||
- Review bylaws and articles of incorporation.
|
||||
- Membership
|
||||
- Solicit the OIYC to become WSCC members (add-on to your OIYC membership?)
|
||||
- We should make it more clear that membership is open to more than WS residents.
|
||||
- Must attract younger people to become members.
|
||||
- DHCC has had success with this. Talk to them about what they did.
|
||||
- Work with realtors to have them introduce new people to the club
|
||||
- Arthur asks to have a physical invitation he can hand out to people he meets.
|
||||
- OIYC Partnership
|
||||
- Kitchen remodel
|
||||
- Articulate shared goals and vision
|
||||
- Ensure the OIYC liaison attends our meetings
|
||||
- One set of common glassware, dishes, etc.
|
||||
- Use/Expectations
|
||||
- Update kitchen instructions (e.g. don’t rinse food down the drain).
|
||||
- Update cleaning rules in hall rent agreement.
|
||||
- Discourage or ban single use plastics during events.
|
||||
- Vision/Goals/Mission/Objectives
|
||||
- Keep the building standing
|
||||
- Grow the organization
|
||||
- Group activity - have members write down a list of what the hall means to them.
|
||||
- Set by example what community can be.
|
||||
|
BIN
src/app/news/2024-03-06-town-hall-meeting/town-hall-meeting.jpg
Normal file
BIN
src/app/news/2024-03-06-town-hall-meeting/town-hall-meeting.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
@ -9,7 +9,7 @@ function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
return (
|
||||
<article className="md:grid md:grid-cols-4 md:items-baseline">
|
||||
<Card className="md:col-span-3">
|
||||
<Card.Title href={`/blog/${article.slug}`}>
|
||||
<Card.Title href={`/news/${article.slug}`}>
|
||||
{article.title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow
|
||||
@ -35,7 +35,7 @@ function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Blog',
|
||||
title: 'Club News',
|
||||
description: 'History, Announcements, and more from the West Sound Hall and Community Club.',
|
||||
}
|
||||
|
||||
@ -44,10 +44,10 @@ export default async function ArticlesIndex() {
|
||||
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="West Sound Hall Blog"
|
||||
title="West Sound Hall News"
|
||||
intro="History, Announcements, and more from the West Sound Hall and Community Club."
|
||||
>
|
||||
<div className="md:border-l md:border-zinc-100 md:pl-6 md:dark:border-zinc-700/40">
|
||||
<div className="md:border-l md:border-zinc-100 md:pl-6">
|
||||
<div className="flex max-w-3xl flex-col space-y-16">
|
||||
{articles.map((article) => (
|
||||
<Article key={article.slug} article={article} />
|
48
src/app/news/welcome-to-the-wscc-website/page.mdx
Normal file
48
src/app/news/welcome-to-the-wscc-website/page.mdx
Normal file
@ -0,0 +1,48 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
import {MdxImage as Image } from '@/components/MdxImage'
|
||||
import originalWebsite from './westsoundhall-original-website.png'
|
||||
|
||||
export const article = {
|
||||
author: 'Tony Grosinger',
|
||||
date: '2024-01-01',
|
||||
title: 'Welcome to the New WSCC Website',
|
||||
description: "In the early 2000's, the West Sound Community Club leadership put a massive effort into improving the hall's physical foundation, however it's easy to overlook the incredible amount of work that was also put into less tangible aspects of the hall.",
|
||||
}
|
||||
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
In the early 2000's, the West Sound Community Club leadership put a massive
|
||||
effort into improving the hall's physical foundation, however it's
|
||||
easy to overlook the incredible amount of work that was also put into less
|
||||
tangible aspects of the hall.
|
||||
|
||||
The original version of this website was launched in 2004. It was one of the
|
||||
first digital archives of the history of West Sound and how central the hall was
|
||||
to this community. This updated version of the website preserves this historical
|
||||
collection and aims to expand it with information collected by the Club over the
|
||||
years.
|
||||
|
||||
|
||||
<div className="not-prose flex flex-col items-center">
|
||||
<Image src={originalWebsite} alt="Screenshot of the original West Sound Hall website" />
|
||||
<span className="mt-2 text-sm text-zinc-400">
|
||||
Screenshot of the original West Sound Hall website.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Along with the website, past Boards have worked to incorporate the Club, make
|
||||
the hall available for rental by the community, and even added the hall to the
|
||||
WA Heritage Register. There's so much more to the hall than just potlucks,
|
||||
and this website hopes to share this with the West Sound Community.
|
||||
|
||||
Thanks for being a part of the West Sound Community!
|
||||
|
||||
For posterity, the source code for this website along with instructions for
|
||||
developing and updating it can be found
|
||||
[here](https://git.grosinger.net/tgrosinger/west-sound-hall).
|
Binary file not shown.
After Width: | Height: | Size: 644 KiB |
@ -5,13 +5,13 @@ export default function NotFound() {
|
||||
return (
|
||||
<Container className="flex h-full items-center pt-16 sm:pt-32">
|
||||
<div className="flex flex-col items-center">
|
||||
<p className="text-base font-semibold text-zinc-400 dark:text-zinc-500">
|
||||
<p className="text-base font-semibold text-zinc-400">
|
||||
404
|
||||
</p>
|
||||
<h1 className="mt-4 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h1 className="mt-4 text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
Page not found
|
||||
</h1>
|
||||
<p className="mt-4 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p className="mt-4 text-base text-zinc-600">
|
||||
Sorry, we couldn’t find the page you’re looking for.
|
||||
</p>
|
||||
<Button href="/" variant="secondary" className="mt-4">
|
||||
|
197
src/app/page.tsx
197
src/app/page.tsx
@ -1,22 +1,37 @@
|
||||
import Image, { type ImageProps } from 'next/image'
|
||||
import clsx from 'clsx'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import Link from 'next/link'
|
||||
import { Card } from '@/components/Card'
|
||||
import { Container } from '@/components/Container'
|
||||
import { CalendarDaysIcon, EnvelopeIcon } from '@heroicons/react/24/solid'
|
||||
import { CalendarDaysIcon } 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'
|
||||
import { getUpcomingEvents, Event } from './calendar/page'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
|
||||
function LinkButton({
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
href: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className="rounded-md px-3 py-2 font-semibold text-center transition bg-sky-300 hover:bg-sky-400"
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title href={`/blog/${article.slug}`}>
|
||||
<Card.Title href={`/news/${article.slug}`}>
|
||||
{article.title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow as="time" dateTime={article.date} decorate>
|
||||
@ -28,134 +43,75 @@ function Article({ article }: { article: BlogPostWithSlug }) {
|
||||
)
|
||||
}
|
||||
|
||||
function Newsletter() {
|
||||
return (
|
||||
<form
|
||||
action="/thank-you"
|
||||
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">
|
||||
<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 about upcoming events and stay in touch with the community.
|
||||
</p>
|
||||
<div className="mt-6 flex">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email address"
|
||||
aria-label="Email address"
|
||||
required
|
||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] shadow-md shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
/>
|
||||
<Button type="submit" className="ml-4 flex-none">
|
||||
Join
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
function EventListItem({ event }: { event: Event }) {
|
||||
const start = dayjs(event.start);
|
||||
const end = dayjs(event.end);
|
||||
|
||||
interface Meeting {
|
||||
title: string
|
||||
date: string
|
||||
startTime: string
|
||||
endTime?: string
|
||||
notes?: string
|
||||
}
|
||||
const date = start.format('YYYY-MM-DD');
|
||||
|
||||
function MeetingListItem({ meeting }: { meeting: Meeting }) {
|
||||
return (
|
||||
<li className="flex gap-4">
|
||||
<dl className="flex flex-auto flex-wrap gap-x-2">
|
||||
<dt className="sr-only">Title</dt>
|
||||
<dd className="w-full flex-none text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{meeting.title}
|
||||
<dd className="w-full flex-none text-sm font-medium text-zinc-900">
|
||||
{event.name}
|
||||
</dd>
|
||||
<dt className="sr-only">Date</dt>
|
||||
<dd className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{meeting.date}
|
||||
<dd className="text-xs text-zinc-500">
|
||||
{date}
|
||||
</dd>
|
||||
<dt className="sr-only">Time</dt>
|
||||
{meeting.endTime
|
||||
{event.allDay
|
||||
? <dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${meeting.startTime} until ${meeting.endTime}`}
|
||||
className="ml-auto text-xs text-zinc-400"
|
||||
aria-label="All day"
|
||||
>
|
||||
<time dateTime={meeting.date + ' ' + meeting.startTime}>{meeting.startTime}</time>{' '}
|
||||
All day
|
||||
</dd>
|
||||
: (event.end
|
||||
? (
|
||||
<dd
|
||||
className="ml-auto text-xs text-zinc-400"
|
||||
aria-label={`${start.format('YYYY-MM-DD HH:mm')} until ${end.format('YYYY-MM-DD HH-mm')}`}
|
||||
>
|
||||
<time dateTime={start.format('YYYY-MM-DD HH:mm')}>{start.format('h:mm a')}</time>{' '}
|
||||
<span aria-hidden="true">—</span>{' '}
|
||||
<time dateTime={meeting.date + ' ' + meeting.endTime}>{meeting.endTime}</time>{' '}
|
||||
<time dateTime={end.format('YYYY-MM-DD HH-mm')}>{end.format('h:mm a')}</time>{' '}
|
||||
</dd>
|
||||
: <dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${meeting.startTime}`}
|
||||
) : (
|
||||
<dd
|
||||
className="ml-auto text-xs text-zinc-400"
|
||||
aria-label={start.format('YYYY-MM-DD HH-mm')}
|
||||
>
|
||||
<time dateTime={meeting.date + ' ' + meeting.startTime}>{meeting.startTime}</time>{' '}
|
||||
<time dateTime={start.format('YYYY-MM-DD HH-mm')}>{start.format('h:mm a')}</time>{' '}
|
||||
</dd>
|
||||
)
|
||||
)
|
||||
}
|
||||
</dl>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function Events() {
|
||||
let events: Array<Meeting> = [
|
||||
{
|
||||
title: 'Christmas Potluck',
|
||||
date: '2023-12-16',
|
||||
startTime: '6:00pm',
|
||||
},
|
||||
{
|
||||
title: 'Member Meeting',
|
||||
date: '2023-12-20',
|
||||
startTime: '6:00pm',
|
||||
endTime: '8:00pm',
|
||||
notes: 'Bring your own chair.'
|
||||
},
|
||||
]
|
||||
async function Events() {
|
||||
const events = await getUpcomingEvents();
|
||||
|
||||
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">
|
||||
<CalendarDaysIcon className="h-6 w-6 flex-none fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500" />
|
||||
<div className="rounded-2xl border border-zinc-100 p-6">
|
||||
<h2 className="flex text-sm font-semibold text-zinc-900">
|
||||
<CalendarDaysIcon className="h-6 w-6 flex-none fill-zinc-100 stroke-zinc-400" />
|
||||
<span className="ml-3">Upcoming Events</span>
|
||||
</h2>
|
||||
<ol className="mt-6 space-y-4">
|
||||
{events.map((meeting, idx) => (
|
||||
<MeetingListItem key={idx} meeting={meeting} />
|
||||
{events.map((event, idx) => (
|
||||
<EventListItem key={idx} event={event} />
|
||||
))}
|
||||
</ol>
|
||||
{/*
|
||||
<Button href="#" variant="secondary" className="group mt-6 w-full">
|
||||
Full Calendar
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Photos() {
|
||||
let rotations = ['rotate-2', '-rotate-2', 'rotate-2', 'rotate-2', '-rotate-2']
|
||||
|
||||
return (
|
||||
<div className="mt-16 sm:mt-20">
|
||||
<div className="-my-4 flex justify-center gap-5 overflow-hidden py-4 sm:gap-8">
|
||||
{[exteriorSoutheastImage, stageImage, exteriorFrontImage, interorEmptyImage, kitchenImage].map((image, imageIndex) => (
|
||||
<div
|
||||
key={image.src}
|
||||
className={clsx(
|
||||
'relative aspect-[9/10] w-44 flex-none overflow-hidden rounded-xl bg-zinc-100 dark:bg-zinc-800 sm:w-72 sm:rounded-2xl',
|
||||
rotations[imageIndex % rotations.length],
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src={image}
|
||||
alt=""
|
||||
sizes="(min-width: 640px) 18rem, 11rem"
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -165,23 +121,37 @@ export default async function Home() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container className="mt-9">
|
||||
<div className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<div className="mx-auto max-w-7xl px-6 mt-24 lg:px-8">
|
||||
<div className="relative px-4 sm:px-8 lg:px-12">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0 grid lg:max-w-none grid-cols-1 lg:grid-cols-2 lg:gap-x-8 gap-y-10">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-6xl lg:col-span-2">
|
||||
West Sound Community Hall
|
||||
</h1>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<div className="max-w-xl">
|
||||
<p className="mt-6 text-base text-zinc-600">
|
||||
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">
|
||||
<p className="mt-6 text-base text-zinc-600">
|
||||
Facing West Sound, the Hall is at the heart of the West Sound community.
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
<Photos />
|
||||
<Image
|
||||
src={exteriorFrontImage}
|
||||
alt="Exterior front of the West Sound Hall"
|
||||
className="lg:row-span-2 w-full max-w-xl rounded-2xl object-cover lg:max-w-none"
|
||||
/>
|
||||
<div className="h-fit max-w-xl rounded-2xl border border-zinc-100 p-6">
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<LinkButton href='/club'>Join or Renew your Membership</LinkButton>
|
||||
<LinkButton href='/rental'>Rent the Hall</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Container className="mt-24 md:mt-28">
|
||||
<div className="mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none lg:grid-cols-2">
|
||||
<div className="flex flex-col gap-16">
|
||||
@ -189,8 +159,7 @@ export default async function Home() {
|
||||
<Article key={article.slug} article={article} />
|
||||
))}
|
||||
</div>
|
||||
<div className="space-y-10 lg:pl-16 xl:pl-24">
|
||||
<Newsletter />
|
||||
<div className="lg:pl-16 xl:pl-24">
|
||||
<Events />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -75,7 +75,7 @@ export default function Projects() {
|
||||
>
|
||||
{projects.map((project) => (
|
||||
<Card as="li" key={project.name}>
|
||||
<div className="relative z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white 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">
|
||||
<div className="relative z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5">
|
||||
<Image
|
||||
src={project.logo}
|
||||
alt=""
|
||||
@ -83,11 +83,11 @@ export default function Projects() {
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<h2 className="mt-6 text-base font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<h2 className="mt-6 text-base font-semibold text-zinc-800">
|
||||
<Card.Link href={project.link.href}>{project.name}</Card.Link>
|
||||
</h2>
|
||||
<Card.Description>{project.description}</Card.Description>
|
||||
<p className="relative z-10 mt-6 flex text-sm font-medium text-zinc-400 transition group-hover:text-teal-500 dark:text-zinc-200">
|
||||
<p className="relative z-10 mt-6 flex text-sm font-medium text-zinc-400 transition group-hover:text-teal-500">
|
||||
<LinkIcon className="h-6 w-6 flex-none" />
|
||||
<span className="ml-2">{project.link.label}</span>
|
||||
</p>
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import { createContext, useEffect, useRef } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { ThemeProvider, useTheme } from 'next-themes'
|
||||
|
||||
function usePrevious<T>(value: T) {
|
||||
let ref = useRef<T>()
|
||||
@ -14,30 +13,6 @@ function usePrevious<T>(value: T) {
|
||||
return ref.current
|
||||
}
|
||||
|
||||
function ThemeWatcher() {
|
||||
let { resolvedTheme, setTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
let media = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
function onMediaChange() {
|
||||
let systemTheme = media.matches ? 'dark' : 'light'
|
||||
if (resolvedTheme === systemTheme) {
|
||||
setTheme('system')
|
||||
}
|
||||
}
|
||||
|
||||
onMediaChange()
|
||||
media.addEventListener('change', onMediaChange)
|
||||
|
||||
return () => {
|
||||
media.removeEventListener('change', onMediaChange)
|
||||
}
|
||||
}, [resolvedTheme, setTheme])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const AppContext = createContext<{ previousPathname?: string }>({})
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
@ -46,10 +21,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ previousPathname }}>
|
||||
<ThemeProvider attribute="class" disableTransitionOnChange>
|
||||
<ThemeWatcher />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</AppContext.Provider>
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import clsx from 'clsx'
|
||||
import { Container } from '@/components/Container'
|
||||
import exteriorFront from '@/images/photos/exterior-front.png'
|
||||
import { EnvelopeIcon, PencilSquareIcon, QuestionMarkCircleIcon } from '@heroicons/react/24/solid'
|
||||
import React from 'react'
|
||||
import { TableCell, TableHeading, TableLeftHeading } from '@/components/Table'
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
@ -22,7 +24,7 @@ function SocialLink({
|
||||
<li className={clsx(className, 'flex')}>
|
||||
<Link
|
||||
href={href}
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500 dark:text-zinc-200 dark:hover:text-teal-500"
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500"
|
||||
>
|
||||
<Icon className="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-teal-500" />
|
||||
<span className="ml-4">{children}</span>
|
||||
@ -32,9 +34,9 @@ function SocialLink({
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'History',
|
||||
title: 'Hall Rental',
|
||||
description:
|
||||
'The history of the West Sound Community Hall on Orcas Island.',
|
||||
'Rental information for the West Sound Hall.',
|
||||
}
|
||||
|
||||
export default function Rental() {
|
||||
@ -47,15 +49,15 @@ export default function Rental() {
|
||||
src={exteriorFront}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="aspect-square rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
className="aspect-square rounded-2xl bg-zinc-100 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
Hall Rental
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
West Sound Hall Rental
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600">
|
||||
<p>
|
||||
The West Sound Community Hall is a public assembly hall, which has
|
||||
been in continuous operation since it was built in 1902. In 1999,
|
||||
@ -78,6 +80,12 @@ export default function Rental() {
|
||||
damage to the Hall or cleaning is necessary, deductions will be
|
||||
made at the discretion of the Board.
|
||||
</p>
|
||||
<p>
|
||||
Please check the
|
||||
<Link href="/calendar" className="pl-1 text-blue-600 hover:underline"
|
||||
>calendar
|
||||
</Link> for availability.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-last sm:order-none lg:pl-20">
|
||||
@ -91,14 +99,14 @@ export default function Rental() {
|
||||
<SocialLink
|
||||
href="mailto:contact@westsoundhall.org"
|
||||
icon={EnvelopeIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
className="mt-8 border-t border-zinc-100 pt-8"
|
||||
>
|
||||
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">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-2xl">
|
||||
Rental Rates
|
||||
</h2>
|
||||
|
||||
@ -107,50 +115,50 @@ export default function Rental() {
|
||||
<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>
|
||||
<th scope="col" className="py-3.5 pl-4 pr-3 sm:pl-0"></th>
|
||||
<TableHeading>Half day (4 hr)</TableHeading>
|
||||
<TableHeading>All day</TableHeading>
|
||||
<TableHeading>Deposit</TableHeading>
|
||||
</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>
|
||||
<TableLeftHeading>Orcas non-profit organizations</TableLeftHeading>
|
||||
<TableCell>$50 or 2 hours for $30*</TableCell>
|
||||
<TableCell>$100</TableCell>
|
||||
<TableCell>None†</TableCell>
|
||||
</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>
|
||||
<TableLeftHeading>WSCC and OIYC Members</TableLeftHeading>
|
||||
<TableCell>$50 or 2 hours for $30*</TableCell>
|
||||
<TableCell>$100</TableCell>
|
||||
<TableCell>None†</TableCell>
|
||||
</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>
|
||||
<TableLeftHeading>Off-island non-profit organizations</TableLeftHeading>
|
||||
<TableCell>$50</TableCell>
|
||||
<TableCell>$100</TableCell>
|
||||
<TableCell>$300</TableCell>
|
||||
</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>
|
||||
<TableLeftHeading>Individuals and non-public use</TableLeftHeading>
|
||||
<TableCell>$40 per hour</TableCell>
|
||||
<TableCell>$300</TableCell>
|
||||
<TableCell>$300</TableCell>
|
||||
</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>
|
||||
<TableLeftHeading>Government sponsored activities</TableLeftHeading>
|
||||
<TableCell>$50</TableCell>
|
||||
<TableCell>$50</TableCell>
|
||||
<TableCell>None†</TableCell>
|
||||
</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">
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-2xl">
|
||||
Use Restrictions
|
||||
</h2>
|
||||
|
||||
@ -168,6 +176,7 @@ export default function Rental() {
|
||||
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:
|
||||
</p>
|
||||
|
||||
<ol className="list-decimal ml-8 mt-5">
|
||||
<li>
|
||||
@ -183,7 +192,6 @@ export default function Rental() {
|
||||
sponsoring nonprofit organization.)
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Any individual or organizational nonpublic use of the hall which
|
||||
@ -214,8 +222,8 @@ export default function Rental() {
|
||||
</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">
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-2xl">
|
||||
Capacity
|
||||
</h2>
|
||||
|
||||
@ -225,8 +233,8 @@ export default function Rental() {
|
||||
</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">
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-2xl">
|
||||
Parking
|
||||
</h2>
|
||||
|
||||
@ -247,8 +255,8 @@ export default function Rental() {
|
||||
</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">
|
||||
<div className="mt-6 space-y-5 text-base text-zinc-600">
|
||||
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 sm:text-2xl">
|
||||
Accessibility
|
||||
</h2>
|
||||
|
||||
|
@ -23,7 +23,7 @@ export default async function ThankYou({
|
||||
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">
|
||||
<p className="mt-6 text-base text-zinc-600">
|
||||
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>
|
||||
|
@ -41,24 +41,24 @@ export function ArticleLayout({
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
aria-label="Go back to articles"
|
||||
className="group mb-8 flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 transition dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 lg:absolute lg:-left-5 lg:-mt-2 lg:mb-0 xl:-top-1.5 xl:left-0 xl:mt-0"
|
||||
className="group mb-8 flex h-10 w-10 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 transition lg:absolute lg:-left-5 lg:-mt-2 lg:mb-0 xl:-top-1.5 xl:left-0 xl:mt-0"
|
||||
>
|
||||
<ArrowLeftIcon className="h-4 w-4 stroke-zinc-500 transition group-hover:stroke-zinc-700 dark:stroke-zinc-500 dark:group-hover:stroke-zinc-400" />
|
||||
<ArrowLeftIcon className="h-4 w-4 stroke-zinc-500 transition group-hover:stroke-zinc-700" />
|
||||
</button>
|
||||
)}
|
||||
<article>
|
||||
<header className="flex flex-col">
|
||||
<h1 className="mt-6 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h1 className="mt-6 text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
{article.title}
|
||||
</h1>
|
||||
<time
|
||||
dateTime={article.date}
|
||||
className="order-first flex items-center text-base text-zinc-400 dark:text-zinc-500"
|
||||
className="order-first flex items-center text-base text-zinc-400"
|
||||
>
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200" />
|
||||
<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>
|
||||
<span id="byline" className="mt-4 text-base text-zinc-400">by {article.author}</span>
|
||||
</header>
|
||||
<Prose className="mt-8" data-mdx-content>
|
||||
{children}
|
||||
|
@ -3,9 +3,9 @@ import clsx from 'clsx'
|
||||
|
||||
const variantStyles = {
|
||||
primary:
|
||||
'bg-zinc-800 font-semibold text-zinc-100 hover:bg-zinc-700 active:bg-zinc-800 active:text-zinc-100/70 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:active:bg-zinc-700 dark:active:text-zinc-100/70',
|
||||
'bg-zinc-800 font-semibold text-zinc-100 hover:bg-zinc-700 active:bg-zinc-800 active:text-zinc-100/70',
|
||||
secondary:
|
||||
'bg-zinc-50 font-medium text-zinc-900 hover:bg-zinc-100 active:bg-zinc-100 active:text-zinc-900/60 dark:bg-zinc-800/50 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:active:bg-zinc-800/50 dark:active:text-zinc-50/70',
|
||||
'bg-zinc-50 font-medium text-zinc-900 hover:bg-zinc-100 active:bg-zinc-100 active:text-zinc-900/60',
|
||||
}
|
||||
|
||||
type ButtonProps = {
|
||||
|
@ -39,7 +39,7 @@ Card.Link = function CardLink({
|
||||
}: React.ComponentPropsWithoutRef<typeof Link>) {
|
||||
return (
|
||||
<>
|
||||
<div className="absolute -inset-x-4 -inset-y-6 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<div className="absolute -inset-x-4 -inset-y-6 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<Link {...props}>
|
||||
<span className="absolute -inset-x-4 -inset-y-6 z-20 sm:-inset-x-6 sm:rounded-2xl" />
|
||||
<span className="relative z-10">{children}</span>
|
||||
@ -59,7 +59,7 @@ Card.Title = function CardTitle<T extends React.ElementType = 'h2'>({
|
||||
let Component = as ?? 'h2'
|
||||
|
||||
return (
|
||||
<Component className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
|
||||
<Component className="text-base font-semibold tracking-tight text-zinc-800">
|
||||
{href ? <Card.Link href={href}>{children}</Card.Link> : children}
|
||||
</Component>
|
||||
)
|
||||
@ -71,7 +71,7 @@ Card.Description = function CardDescription({
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<p className="relative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<p className="relative z-10 mt-2 text-sm text-zinc-600">
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
@ -105,7 +105,7 @@ Card.Eyebrow = function CardEyebrow<T extends React.ElementType = 'p'>({
|
||||
<Component
|
||||
className={clsx(
|
||||
className,
|
||||
'relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500',
|
||||
"relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400",
|
||||
decorate && 'pl-3.5',
|
||||
)}
|
||||
{...props}
|
||||
@ -115,7 +115,7 @@ Card.Eyebrow = function CardEyebrow<T extends React.ElementType = 'p'>({
|
||||
className="absolute inset-y-0 left-0 flex items-center"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
||||
<span className="h-4 w-0.5 rounded-full bg-zinc-200" />
|
||||
</span>
|
||||
)}
|
||||
{children}
|
||||
|
@ -12,7 +12,7 @@ function NavLink({
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className="transition hover:text-teal-500 dark:hover:text-teal-400"
|
||||
className="transition hover:text-teal-500"
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
@ -23,17 +23,24 @@ export function Footer() {
|
||||
return (
|
||||
<footer className="mt-32 flex-none">
|
||||
<ContainerOuter>
|
||||
<div className="border-t border-zinc-100 pb-16 pt-10 dark:border-zinc-700/40">
|
||||
<div className="border-t border-zinc-100 pb-16 pt-10">
|
||||
<ContainerInner>
|
||||
<div className="flex flex-col items-center justify-between gap-6 sm:flex-row">
|
||||
<div className="flex flex-wrap justify-center gap-x-6 gap-y-1 text-sm font-medium text-zinc-800 dark:text-zinc-200">
|
||||
<div className="flex flex-wrap justify-center gap-x-6 gap-y-1 text-sm font-medium text-zinc-800">
|
||||
<NavLink href="/hall-history">History</NavLink>
|
||||
<NavLink href="/news">News</NavLink>
|
||||
<NavLink href="/calendar">Calendar</NavLink>
|
||||
<NavLink href="/rental">Rental</NavLink>
|
||||
<NavLink href="/club">Club</NavLink>
|
||||
</div>
|
||||
<p className="text-sm text-zinc-400 dark:text-zinc-500">
|
||||
<div>
|
||||
<p className="text-sm text-zinc-400">
|
||||
© {new Date().getFullYear()} West Sound Community Club. All rights reserved.
|
||||
</p>
|
||||
<p className="text-sm text-zinc-400">
|
||||
WSCC is a 501c3 nonprofit organization - 91-1283768
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ContainerInner>
|
||||
</div>
|
||||
|
File diff suppressed because one or more lines are too long
@ -6,7 +6,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
||||
<>
|
||||
<div className="fixed inset-0 flex justify-center sm:px-8">
|
||||
<div className="flex w-full max-w-7xl lg:px-8">
|
||||
<div className="w-full bg-white ring-1 ring-zinc-100 dark:bg-zinc-900 dark:ring-zinc-300/20" />
|
||||
<div className="w-full bg-white ring-1 ring-zinc-100" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex w-full flex-col">
|
||||
|
7
src/components/MdxImage.tsx
Normal file
7
src/components/MdxImage.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import React from 'react'
|
||||
import Image, { ImageProps } from 'next/image'
|
||||
|
||||
// Workaround for https://github.com/hashicorp/next-mdx-remote/issues/405
|
||||
export function MdxImage(props: ImageProps) {
|
||||
return <Image {...props} alt={props.alt} />;
|
||||
}
|
@ -5,6 +5,6 @@ export function Prose({
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<'div'>) {
|
||||
return (
|
||||
<div className={clsx(className, 'prose dark:prose-invert')} {...props} />
|
||||
<div className={clsx(className, 'prose')} {...props} />
|
||||
)
|
||||
}
|
||||
|
@ -12,12 +12,12 @@ export function Section({
|
||||
return (
|
||||
<section
|
||||
aria-labelledby={id}
|
||||
className="md:border-l md:border-zinc-100 md:pl-6 md:dark:border-zinc-700/40"
|
||||
className="md:border-l md:border-zinc-100 md:pl-6"
|
||||
>
|
||||
<div className="grid max-w-3xl grid-cols-1 items-baseline gap-y-8 md:grid-cols-4">
|
||||
<h2
|
||||
id={id}
|
||||
className="text-sm font-semibold text-zinc-800 dark:text-zinc-100"
|
||||
className="text-sm font-semibold text-zinc-800"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
@ -12,10 +12,10 @@ export function SimpleLayout({
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
<header className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl">
|
||||
{title}
|
||||
</h1>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p className="mt-6 text-base text-zinc-600">
|
||||
{intro}
|
||||
</p>
|
||||
</header>
|
||||
|
25
src/components/Table.tsx
Normal file
25
src/components/Table.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
export function TableHeading({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{children}</th>
|
||||
|
||||
}
|
||||
|
||||
export function TableLeftHeading({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">{children}</td>
|
||||
}
|
||||
|
||||
export function TableCell({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{children}</td>
|
||||
}
|
@ -12,7 +12,7 @@ export interface BlogPostWithSlug extends BlogPost {
|
||||
}
|
||||
|
||||
async function importBlogPost(filename: string): Promise<BlogPostWithSlug> {
|
||||
let { article } = (await import(`../app/blog/${filename}`)) as {
|
||||
let { article } = (await import(`../app/news/${filename}`)) as {
|
||||
default: React.ComponentType;
|
||||
article: BlogPost;
|
||||
};
|
||||
@ -25,7 +25,7 @@ async function importBlogPost(filename: string): Promise<BlogPostWithSlug> {
|
||||
|
||||
export async function getAllBlogPosts() {
|
||||
let articleFilenames = await glob('*/page.mdx', {
|
||||
cwd: './src/app/blog',
|
||||
cwd: './src/app/news',
|
||||
});
|
||||
|
||||
const posts = await Promise.all(articleFilenames.map(importBlogPost));
|
||||
|
@ -2,3 +2,10 @@
|
||||
@import 'tailwindcss/components';
|
||||
@import './prism.css';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
:root {
|
||||
/* Your default theme */
|
||||
|
||||
--stripe-background: #FFFFFF;
|
||||
--stripe-foreground: #000000;
|
||||
}
|
@ -170,8 +170,8 @@ export default function typographyStyles({ theme }: PluginUtils) {
|
||||
paddingLeft: theme('spacing.6'),
|
||||
},
|
||||
li: {
|
||||
marginTop: theme('spacing.6'),
|
||||
marginBottom: theme('spacing.6'),
|
||||
// marginTop: theme('spacing.2'),
|
||||
// marginBottom: theme('spacing.2'),
|
||||
paddingLeft: theme('spacing[3.5]'),
|
||||
},
|
||||
'li::marker': {
|
||||
@ -184,14 +184,14 @@ export default function typographyStyles({ theme }: PluginUtils) {
|
||||
'ul > li::marker': {
|
||||
color: 'var(--tw-prose-bullets)',
|
||||
},
|
||||
'li :is(ol, ul)': {
|
||||
marginTop: theme('spacing.4'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
'li :is(li, p)': {
|
||||
marginTop: theme('spacing.3'),
|
||||
marginBottom: theme('spacing.3'),
|
||||
},
|
||||
// 'li :is(ol, ul)': {
|
||||
// marginTop: theme('spacing.2'),
|
||||
// marginBottom: theme('spacing.2'),
|
||||
// },
|
||||
// 'li :is(li, p)': {
|
||||
// marginTop: theme('spacing.3'),
|
||||
// marginBottom: theme('spacing.3'),
|
||||
// },
|
||||
|
||||
// Code blocks
|
||||
pre: {
|
||||
|
Reference in New Issue
Block a user