5 Commits

Author SHA1 Message Date
f62e434dff Fix club membership form in dark mode
All checks were successful
Build Production Image / Build Production Image (push) Successful in 53s
2024-02-01 19:55:11 -08:00
7190b306e7 Add EIN to footer
All checks were successful
Build Production Image / Build Production Image (push) Successful in 1m3s
2024-01-31 20:29:43 -08:00
d9225e36d7 Fix rental table styling in dark mode 2024-01-31 20:29:27 -08:00
c957b156d8 Fix invisible heading in dark mode 2024-01-31 20:29:03 -08:00
f936bae555 Add board of directors page 2024-01-31 20:28:13 -08:00
8 changed files with 293 additions and 64 deletions

View 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 dark:text-zinc-200 dark: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 dark:text-zinc-100 sm:text-5xl">
The Board of Directors
</h1>
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
<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>
<th scope="col" className="py-3.5 pl-4 pr-3 sm:pl-0">Name</th>
<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 dark:border-zinc-700/40"
>
contact@westsoundhall.org
</SocialLink>
<SocialLink
href="mailto:contact@westsoundhall.org"
icon={EnvelopeIcon}
className="mt-4 border-zinc-100 dark:border-zinc-700/40"
>
board@westsoundhall.org
</SocialLink>
</ul>
</div>
</div>
</Container>
)
}

View File

@ -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, GiftIcon, EnvelopeIcon, UserGroupIcon } from '@heroicons/react/24/solid'
import interiorEmptyImage from '@/images/photos/interior-empty.jpg'
import { ClubPayment } from './payment';
function SocialLink({
className,
@ -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">
@ -98,6 +107,9 @@ export default function Club() {
>
contact@westsoundhall.org
</SocialLink>
<SocialLink href="/board-of-directors" icon={UserGroupIcon} className="mt-4">
Board of Directors
</SocialLink>
</ul>
</div>
<div>

View File

@ -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 {
@ -166,7 +166,7 @@ function CheckoutForm({
{/* Membership Type */}
<RadioGroup value={selectedMembershipLevel} onChange={setSelectedMembershipLevel} className="space-y-3">
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Select a membership type
</RadioGroup.Label>
@ -177,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 dark:border-gray-500',
'relative flex cursor-pointer rounded-lg border bg-white hover:bg-gray-50 hover:dark:bg-zinc-600 dark:bg-zinc-700 p-4 shadow-sm focus:outline-none'
)
}
>
@ -186,7 +186,7 @@ function CheckoutForm({
<>
<span className="flex flex-1 items-center justify-between ">
<span className="flex flex-col">
<RadioGroup.Label as="span" className="block text-sm font-medium text-gray-900">
<RadioGroup.Label as="span" className="block text-sm font-medium text-gray-900 dark:text-white">
{membership.title}
</RadioGroup.Label>
<RadioGroup.Description as="span" className="mt-1 flex items-center text-sm text-gray-500">
@ -194,7 +194,7 @@ function CheckoutForm({
</RadioGroup.Description>
</span>
<RadioGroup.Description as="span" className="ml-8 text-sm font-medium">
<span className="text-gray-900">${membership.price}</span>
<span className="text-gray-900 dark:text-white">${membership.price}</span>
<span className="text-gray-500">/yr</span>
</RadioGroup.Description>
</span>
@ -215,7 +215,7 @@ function CheckoutForm({
{/* Additional donation */}
<RadioGroup value={selectedAdditionalDonation} onChange={setSelectedAdditionalDonation} className="space-y-3">
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900">
<RadioGroup.Label className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Additional donation
</RadioGroup.Label>
<div className="grid grid-cols-3 gap-3 sm:grid-cols-5">
@ -228,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 dark:ring-gray-500 text-gray-900 dark:text-white hover:bg-gray-50 hover:dark:bg-zinc-600',
'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1 bg-white dark:bg-zinc-700'
)
}
>
@ -250,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 dark:text-white dark:bg-zinc-700 ring-1 ring-inset ring-gray-200 dark:ring-gray-500 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"
/>
@ -274,12 +274,12 @@ function CheckoutForm({
</RadioGroup>
<div className="space-y-3">
<h2 className="text-base font-semibold leading-6 text-gray-900">
<h2 className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
About you
</h2>
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-indigo-600">
<label htmlFor="name" className="block text-xs font-medium text-gray-900">
<div className="rounded-md mb-3 px-3 pb-1.5 pt-2.5 shadow-sm ring-1 ring-inset ring-gray-200 dark:ring-gray-500 focus-within:ring-2 focus-within:ring-indigo-600 dark:bg-zinc-700 dark:text-white">
<label htmlFor="name" className="block text-xs font-medium text-gray-900 dark:text-zinc-400">
Email
</label>
<input
@ -288,7 +288,7 @@ function CheckoutForm({
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="block w-full border-0 p-0 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
className="block w-full border-0 p-0 dark:bg-zinc-700 text-gray-900 dark:text-white placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
placeholder="you@example.com"
/>
</div>
@ -296,7 +296,7 @@ function CheckoutForm({
</div>
<div className="space-y-3">
<h2 className="text-base font-semibold leading-6 text-gray-900">
<h2 className="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Payment
</h2>
<div className="mt-1 text-sm text-gray-500">
@ -329,9 +329,37 @@ 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];
const darkTheme = htmlEl.classList.contains("dark");
useEffect(() => {
// Create PaymentIntent as soon as the page loads using our local API
fetch('api/stripe_intent', {
@ -349,15 +377,36 @@ 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 cb = useCallback(
() => {
const updatedHtmlEl = document.getElementsByTagName('html')[0];
const updatedDarkTheme = updatedHtmlEl.classList.contains("dark");
if (updatedDarkTheme !== darkTheme) {
location.reload();
}
},
[darkTheme]
)
useMutationObservable(htmlEl, cb);
const options: StripeElementsOptions = {
clientSecret,
appearance
appearance,
}
return <>
{clientSecret && (
<Elements options={options}

View File

@ -143,7 +143,7 @@ export default async function Home() {
<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-gray-900 sm:text-6xl lg:col-span-2">
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-6xl lg:col-span-2">
West Sound Community Hall
</h1>
<div className="max-w-xl">

View File

@ -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,
@ -107,42 +109,42 @@ 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">
<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>
@ -168,6 +170,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 +186,6 @@ export default function Rental() {
sponsoring nonprofit organization.)
</li>
</ol>
</p>
<p>
Any individual or organizational nonpublic use of the hall which

View File

@ -32,9 +32,14 @@ export function Footer() {
<NavLink href="/rental">Rental</NavLink>
<NavLink href="/club">Club</NavLink>
</div>
<div>
<p className="text-sm text-zinc-400 dark:text-zinc-500">
&copy; {new Date().getFullYear()} West Sound Community Club. All rights reserved.
</p>
<p className="text-sm text-zinc-400 dark:text-zinc-500">
WSCC is a 501c3 nonprofit organization - 91-1283768
</p>
</div>
</div>
</ContainerInner>
</div>

25
src/components/Table.tsx Normal file
View 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 dark:text-zinc-100">{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 dark:text-zinc-100 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 dark:text-zinc-400">{children}</td>
}

View File

@ -2,3 +2,15 @@
@import 'tailwindcss/components';
@import './prism.css';
@import 'tailwindcss/utilities';
:root {
/* Your default theme */
--stripe-background: #FFFFFF;
--stripe-foreground: #000000;
}
.dark {
--stripe-background: #3f3f46;
--stripe-foreground: #E4E4E7;
}