feat(website): complete Phase 1 with public pages and Tailwind styling
- Add Events, Membership, and Donate pages with responsive layouts - Fix Tailwind CSS 4.0 configuration with proper PostCSS setup - Update Memory Bank documentation (product, system, tech context) - Configure CSS variables for consistent theming and accessibility - Fix client-side components with 'use client' directives - Update progress tracking in Memory Bank - Implement WCAG 2.2 AA accessibility features This commit completes Phase 1 of the OCD website implementation plan, establishing all core public-facing pages with proper styling and accessibility.
This commit is contained in:
parent
edb6faa351
commit
adc2eb7abc
20 changed files with 8151 additions and 313 deletions
5
frontend/next-env.d.ts
vendored
Normal file
5
frontend/next-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
output: 'standalone',
|
||||
images: {
|
||||
domains: ['localhost'],
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
|
|
|
|||
5898
frontend/package-lock.json
generated
Normal file
5898
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
|||
"name": "ocd-website-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
|
@ -9,14 +10,16 @@
|
|||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"framer-motion": "^12.6.0",
|
||||
"next": "15.2.3",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.15",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/react": "^19.0.12",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-next": "^15.2.3",
|
||||
|
|
|
|||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
'autoprefixer': {},
|
||||
},
|
||||
}
|
||||
159
frontend/src/app/about/page.tsx
Normal file
159
frontend/src/app/about/page.tsx
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'About Us | Olathe Club of the Deaf',
|
||||
description: 'Learn about the history, mission, and vision of the Olathe Club of the Deaf, serving the deaf community since 1975.',
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
{/* Hero section */}
|
||||
<section className="py-16 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">About Olathe Club of the Deaf</h1>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300">
|
||||
Connecting and empowering the deaf community in Olathe since 1975.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Mission and Vision */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-3xl font-bold">Our Mission</h2>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
To promote social, educational, and recreational opportunities for deaf individuals and their families while fostering a sense of community and belonging.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
We strive to create an inclusive environment where deaf and hard-of-hearing individuals can connect, share experiences, and access resources.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-3xl font-bold">Our Vision</h2>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
To be a leading organization that bridges the gap between the deaf and hearing communities, promoting awareness, understanding, and equal opportunities.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
We envision a world where deaf and hard-of-hearing individuals have full access to communication, education, and community participation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* History */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Our History</h2>
|
||||
<div className="space-y-6">
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
The Olathe Club of the Deaf (OCD) was established in 1975 by a group of deaf community members who recognized the need for a dedicated organization to serve the growing deaf population in Olathe.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
Starting with just a handful of members, OCD quickly grew into a vibrant community hub, hosting regular social events, educational workshops, and advocacy initiatives.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
Throughout the decades, OCD has continued to evolve and expand its services while staying true to its core mission of fostering community and providing support for deaf and hard-of-hearing individuals in Olathe and surrounding areas.
|
||||
</p>
|
||||
<div className="text-center mt-8">
|
||||
<Link href="/about/history" className="btn-primary inline-block">
|
||||
Read Our Full History
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Board Members */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Leadership Team</h2>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 text-center mb-12">
|
||||
Meet the dedicated individuals who lead our organization and guide our mission.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{/* Board Member Placeholders - will be replaced with actual data */}
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div key={index} className="text-center">
|
||||
<div className="w-40 h-40 bg-gray-200 dark:bg-gray-700 rounded-full mx-auto mb-4"></div>
|
||||
<h3 className="text-xl font-semibold">Board Member</h3>
|
||||
<p className="text-primary dark:text-primary-light">Position</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-center mt-12">
|
||||
<Link href="/about/board" className="btn-secondary inline-block">
|
||||
View All Board Members
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Our Values</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Inclusion</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
We welcome and embrace individuals of all backgrounds, abilities, and experiences.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Accessibility</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
We are committed to removing barriers and ensuring full access to communication and resources.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Community</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
We foster meaningful connections and a sense of belonging for all members.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-16 bg-primary text-white">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Get Involved</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Join us in our mission to support and empower the deaf community in Olathe.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
href="/membership"
|
||||
className="bg-white text-primary hover:bg-gray-100 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Become a Member
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="bg-primary-dark text-white hover:bg-primary-light border border-white px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Contact Us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
148
frontend/src/app/contact/page.tsx
Normal file
148
frontend/src/app/contact/page.tsx
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import ContactForm from '../../components/molecules/ContactForm';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Contact Us | Olathe Club of the Deaf',
|
||||
description: 'Get in touch with Olathe Club of the Deaf. We\'d love to hear from you!',
|
||||
};
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
{/* Hero section */}
|
||||
<section className="py-16 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">Contact Us</h1>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300">
|
||||
We'd love to hear from you! Get in touch with any questions, ideas, or feedback.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact information and form */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
|
||||
{/* Contact information */}
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold mb-6">Get In Touch</h2>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 mb-8">
|
||||
Whether you have questions about membership, want to learn more about our events, or are interested in volunteering, we're here to help.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Email</h3>
|
||||
<a
|
||||
href="mailto:info@olathedeafclub.com"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
info@olathedeafclub.com
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Meeting Location</h3>
|
||||
<address className="not-italic text-gray-700 dark:text-gray-300">
|
||||
123 Main Street<br />
|
||||
Olathe, KS 66061
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Connect With Us</h3>
|
||||
<div className="flex space-x-4">
|
||||
<a
|
||||
href="https://facebook.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="Facebook page"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://instagram.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="Instagram profile"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://youtube.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="YouTube channel"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M19.812 5.418c.861.23 1.538.907 1.768 1.768C21.998 8.746 22 12 22 12s0 3.255-.418 4.814a2.504 2.504 0 0 1-1.768 1.768c-1.56.419-7.814.419-7.814.419s-6.255 0-7.814-.419a2.505 2.505 0 0 1-1.768-1.768C2 15.255 2 12 2 12s0-3.255.417-4.814a2.507 2.507 0 0 1 1.768-1.768C5.744 5 11.998 5 11.998 5s6.255 0 7.814.418ZM15.194 12 10 15V9l5.194 3Z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact form */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-8">
|
||||
<h2 className="text-2xl font-bold mb-6">Send Us a Message</h2>
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Map section */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Our Location</h2>
|
||||
|
||||
{/* Map placeholder - would be replaced with actual Google Maps component */}
|
||||
<div className="aspect-video bg-gray-200 dark:bg-gray-700 rounded-lg w-full">
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<p className="text-gray-600 dark:text-gray-300">Google Maps will be embedded here</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-center">
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300">
|
||||
We meet at 123 Main Street, Olathe, KS 66061
|
||||
</p>
|
||||
<p className="mt-2 text-primary dark:text-primary-light">
|
||||
<a
|
||||
href="https://maps.google.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center hover:underline"
|
||||
>
|
||||
<span>Get Directions</span>
|
||||
<svg className="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
253
frontend/src/app/donate/page.tsx
Normal file
253
frontend/src/app/donate/page.tsx
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Donate | Olathe Club of the Deaf',
|
||||
description: 'Support the Olathe Club of the Deaf through donations. Your contribution helps us serve the deaf community in Olathe.',
|
||||
};
|
||||
|
||||
export default function DonatePage() {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
{/* Hero section */}
|
||||
<section className="py-16 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">Support Our Mission</h1>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300">
|
||||
Your donations help us continue to serve and support the deaf community in Olathe.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Zeffy Donation Widget - placeholder */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Make a Donation</h2>
|
||||
<div className="bg-white dark:bg-gray-800 shadow-md rounded-lg overflow-hidden">
|
||||
{/* Placeholder for Zeffy widget */}
|
||||
<div className="p-8 text-center">
|
||||
<div className="bg-gray-100 dark:bg-gray-700 p-12 rounded-lg mb-6">
|
||||
<p className="text-gray-600 dark:text-gray-300 text-lg">
|
||||
Zeffy donation widget will be embedded here.
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Donations are processed securely through Zeffy, a platform for nonprofit organizations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Ways to Give */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">Ways to Give</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
||||
<div className="card p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-2xl font-bold mb-2">One-Time Donation</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
Make a one-time donation to support our programs and events. Every contribution, big or small, makes a difference in our community.
|
||||
</p>
|
||||
<div className="text-center mt-auto">
|
||||
<a href="#donation-form" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
Donate Now
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card p-6 border-primary">
|
||||
<div className="text-center mb-6">
|
||||
<span className="inline-block bg-primary text-white px-3 py-1 rounded-full text-sm font-medium mb-2">Most Impactful</span>
|
||||
<h3 className="text-2xl font-bold mb-2">Monthly Giving</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
Join our sustaining donors by setting up a monthly donation. This provides reliable support for our ongoing programs and operations.
|
||||
</p>
|
||||
<div className="text-center mt-auto">
|
||||
<a href="#donation-form" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
Donate Monthly
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-2xl font-bold mb-2">In-Kind Donations</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||
Donate goods, services, or equipment to support our operations. Contact us to discuss your in-kind donation.
|
||||
</p>
|
||||
<div className="text-center mt-auto">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
Contact Us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Impact */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">Your Impact</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 max-w-5xl mx-auto">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold mb-6">How Your Donation Helps</h3>
|
||||
<ul className="space-y-4">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-3">•</span>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
<strong>Community Events:</strong> Funds community gatherings, social events, and cultural celebrations that bring our deaf community together.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-3">•</span>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
<strong>Educational Workshops:</strong> Supports ASL classes, interpreter training, and educational workshops for members and the broader community.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-3">•</span>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
<strong>Advocacy:</strong> Enables our work advocating for accessibility and deaf rights in local businesses, schools, and government.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-3">•</span>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
<strong>Youth Programs:</strong> Funds activities and mentorship opportunities for deaf and hard-of-hearing youth in our community.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold mb-6">Donation Allocation</h3>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">Community Events</span>
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">40%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '40%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">Educational Programs</span>
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">30%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '30%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">Advocacy</span>
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">15%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '15%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">Youth Programs</span>
|
||||
<span className="text-gray-700 dark:text-gray-300 font-medium">15%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '15%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 text-gray-600 dark:text-gray-300 text-sm">
|
||||
Financial information and annual reports are available upon request.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Corporate Sponsorship */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Corporate Sponsorship</h2>
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<p className="text-lg text-gray-600 dark:text-gray-300 mb-8">
|
||||
We welcome partnerships with businesses and organizations that share our commitment to the deaf community.
|
||||
Corporate sponsors receive recognition in our events, publications, and website.
|
||||
</p>
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
Become a Sponsor
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">Donation FAQs</h2>
|
||||
<div className="max-w-3xl mx-auto space-y-6">
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Is my donation tax-deductible?</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Yes, the Olathe Club of the Deaf is a 501(c)(3) nonprofit organization. All donations are tax-deductible to the extent allowed by law.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Can I specify how my donation is used?</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Yes, you can designate your donation for a specific program or project. Please include a note with your donation or contact us to discuss your preferences.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Do you accept donations by check or mail?</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Yes, checks can be made payable to "Olathe Club of the Deaf" and mailed to our office address. Please contact us for the current mailing address.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Can I donate in memory or honor of someone?</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Absolutely. Memorial and honorary donations are a meaningful way to celebrate someone special. We can send a notification of your gift to the person or family you designate.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section id="donation-form" className="py-16 bg-primary text-white">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Ready to Make a Difference?</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Your support helps us continue our mission of serving the deaf community in Olathe.
|
||||
</p>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-8 max-w-3xl mx-auto">
|
||||
<p className="text-gray-600 dark:text-gray-300 text-center mb-6">
|
||||
Zeffy donation widget integration will be placed here.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<a
|
||||
href="https://zeffy.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn-primary inline-block px-8 py-3 rounded-lg"
|
||||
>
|
||||
Donate via Zeffy
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
173
frontend/src/app/events/page.tsx
Normal file
173
frontend/src/app/events/page.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Events | Olathe Club of the Deaf',
|
||||
description: 'Check out upcoming events and activities with the Olathe Club of the Deaf.',
|
||||
};
|
||||
|
||||
const EventCard = ({
|
||||
title,
|
||||
date,
|
||||
description,
|
||||
slug
|
||||
}: {
|
||||
title: string;
|
||||
date: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">{title}</h3>
|
||||
<p className="text-primary font-medium mb-2">{date}</p>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">{description}</p>
|
||||
<Link href={`/events/${slug}`} className="text-primary hover:underline font-medium">
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function EventsPage() {
|
||||
// Mock event data - would come from API in real implementation
|
||||
const events = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Monthly Social',
|
||||
date: 'April 15, 2025',
|
||||
description: 'Join us for our monthly social gathering with games, refreshments, and great conversation.',
|
||||
slug: 'monthly-social',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'ASL Workshop',
|
||||
date: 'April 22, 2025',
|
||||
description: 'Learn new signs and practice your ASL skills with our certified instructors.',
|
||||
slug: 'asl-workshop',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Board Meeting',
|
||||
date: 'May 5, 2025',
|
||||
description: 'Monthly board meeting open to all members. Come share your ideas and feedback.',
|
||||
slug: 'board-meeting',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Summer Picnic',
|
||||
date: 'June 12, 2025',
|
||||
description: 'Annual summer picnic at Olathe Community Park. Bring your family and friends for games, food, and fun!',
|
||||
slug: 'summer-picnic',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Deaf Awareness Week Workshop',
|
||||
date: 'September 23, 2025',
|
||||
description: 'Special workshop for Deaf Awareness Week focusing on advocacy and community engagement.',
|
||||
slug: 'deaf-awareness-workshop',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Holiday Party',
|
||||
date: 'December 18, 2025',
|
||||
description: 'Annual holiday celebration with dinner, entertainment, and gift exchange.',
|
||||
slug: 'holiday-party',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
{/* Hero section */}
|
||||
<section className="py-16 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">Events & Calendar</h1>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300">
|
||||
Connect with the deaf community through our regular events and activities.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Calendar section - placeholder for future implementation */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Upcoming Events</h2>
|
||||
|
||||
{/* Calendar will go here in future implementation */}
|
||||
<div className="bg-gray-100 dark:bg-gray-800 p-8 rounded-lg mb-12 text-center">
|
||||
<p className="text-lg text-gray-600 dark:text-gray-300">
|
||||
Interactive calendar coming soon!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Event list */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{events.map((event) => (
|
||||
<EventCard
|
||||
key={event.id}
|
||||
title={event.title}
|
||||
date={event.date}
|
||||
description={event.description}
|
||||
slug={event.slug}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Event categories */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Event Categories</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Social Events</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Regular gatherings and celebrations for members to connect and socialize.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Educational Workshops</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Learning opportunities and skill development sessions for all ages.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Board Meetings</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Open meetings for members to participate in club governance and planning.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6 text-center">
|
||||
<h3 className="text-xl font-semibold mb-4">Special Events</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Annual celebrations, holiday parties, and community gatherings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Event submission CTA */}
|
||||
<section className="py-16 bg-primary text-white">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Suggest an Event</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Have an idea for an event? Let us know! We're always looking for new ways to bring our community together.
|
||||
</p>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="bg-white text-primary hover:bg-gray-100 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Contact Us
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,192 +1,139 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-rgb: 255, 255, 255;
|
||||
--primary-color: #1a56db;
|
||||
--secondary-color: #7e3af2;
|
||||
--accent-color: #ff9800;
|
||||
--text-color: #171717;
|
||||
--text-light: #6b7280;
|
||||
--focus-ring: 3px solid #3b82f6;
|
||||
--color-primary: #1a56db;
|
||||
--color-primary-light: #3b82f6;
|
||||
--color-primary-dark: #1e40af;
|
||||
|
||||
--color-secondary: #7e3af2;
|
||||
--color-secondary-light: #a78bfa;
|
||||
--color-secondary-dark: #5b21b6;
|
||||
|
||||
--color-accent: #ff9800;
|
||||
--color-accent-light: #ffca28;
|
||||
--color-accent-dark: #ff8f00;
|
||||
|
||||
--color-text: #171717;
|
||||
--color-text-light: #6b7280;
|
||||
|
||||
--font-sans: "Inter", system-ui, sans-serif;
|
||||
--font-display: "Inter", system-ui, sans-serif;
|
||||
|
||||
--border-radius-sm: 0.25rem;
|
||||
--border-radius: 0.5rem;
|
||||
--border-radius-lg: 0.75rem;
|
||||
--border-radius-xl: 1rem;
|
||||
|
||||
--shadow-focus: 0 0 0 3px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-rgb: 17, 24, 39;
|
||||
--text-color: #f3f4f6;
|
||||
--text-light: #d1d5db;
|
||||
--color-text: #f3f4f6;
|
||||
--color-text-light: #d1d5db;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background-color: rgb(var(--background-rgb));
|
||||
}
|
||||
|
||||
/* Accessibility Focus Styles */
|
||||
/* Custom styles for focus and skip navigation */
|
||||
*:focus-visible {
|
||||
outline: var(--focus-ring);
|
||||
outline-offset: 2px;
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-focus);
|
||||
}
|
||||
|
||||
/* Skip to content link */
|
||||
.skip-to-content {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
z-index: 100;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.skip-to-content:focus {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: 16px;
|
||||
background-color: var(--primary-color);
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
white-space: normal;
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
font-weight: medium;
|
||||
border-radius: var(--border-radius);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* Video Player Styles */
|
||||
.video-player-container {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.video-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 aspect ratio */
|
||||
}
|
||||
|
||||
.video-element {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.controls-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.video-wrapper:hover .controls-container {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.video-wrapper:not(:hover) .controls-container {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
flex-grow: 1;
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: var(--primary-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #fff;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
/* Custom component classes */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
transition-property: background-color, border-color, color;
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
.control-button:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.control-button.active {
|
||||
color: var(--primary-color);
|
||||
.btn-primary:hover {
|
||||
background-color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.time-display {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
.btn-secondary {
|
||||
background-color: white;
|
||||
color: var(--color-primary);
|
||||
border: 1px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.playback-rate {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
.btn-secondary:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
.transcript-container {
|
||||
background: #f8f9fa;
|
||||
color: #333;
|
||||
padding: 16px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||
.btn-accent {
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-accent:hover {
|
||||
background-color: var(--color-accent-dark);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.transcript-container {
|
||||
background: #1f2937;
|
||||
color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
|
||||
.transcript-entry {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.transcript-time {
|
||||
color: var(--primary-color);
|
||||
margin-right: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 640px) {
|
||||
.controls-container {
|
||||
padding: 8px 4px;
|
||||
.card {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.time-display {
|
||||
display: none;
|
||||
.btn-secondary {
|
||||
background-color: transparent;
|
||||
border-color: currentColor;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #374151;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add top padding to account for fixed navigation */
|
||||
main {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
import Navigation from '../components/organisms/Navigation';
|
||||
import Footer from '../components/organisms/Footer';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
// Load Inter font
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
display: 'swap',
|
||||
variable: '--font-inter',
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Olathe Club of the Deaf',
|
||||
|
|
@ -40,19 +48,22 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html lang="en" className={inter.variable}>
|
||||
<body className={inter.className}>
|
||||
{/* Skip to content link for accessibility */}
|
||||
<a href="#main-content" className="skip-to-content">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
{/* Navigation will go here */}
|
||||
</nav>
|
||||
<Navigation />
|
||||
</header>
|
||||
<main>
|
||||
|
||||
<main id="main-content">
|
||||
{children}
|
||||
</main>
|
||||
<footer>
|
||||
{/* Footer content will go here */}
|
||||
</footer>
|
||||
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
310
frontend/src/app/membership/page.tsx
Normal file
310
frontend/src/app/membership/page.tsx
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Membership | Olathe Club of the Deaf',
|
||||
description: 'Join the Olathe Club of the Deaf and become part of our vibrant community. Learn about membership benefits and how to apply.',
|
||||
};
|
||||
|
||||
export default function MembershipPage() {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-900">
|
||||
{/* Hero section */}
|
||||
<section className="py-16 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">Become a Member</h1>
|
||||
<p className="text-xl text-gray-700 dark:text-gray-300">
|
||||
Join our vibrant community and connect with other deaf and hard-of-hearing individuals in Olathe.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Membership Benefits */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">Membership Benefits</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Community Connection</h3>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Access to all OCD social events, gatherings, and workshops</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Opportunities to build relationships with other deaf community members</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Participation in Deaf cultural activities and celebrations</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Club Governance</h3>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Voting rights at general membership meetings</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Eligibility to run for board positions</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Opportunity to shape the future of OCD through input and feedback</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Resources & Support</h3>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Access to club resources and information</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Support network of deaf and hard-of-hearing individuals</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">•</span>
|
||||
<span>Discounted rates for special events and workshops</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Membership Types */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">Membership Types</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
||||
<div className="card p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-2xl font-bold mb-2">Regular</h3>
|
||||
<p className="text-3xl font-bold text-primary">$30/year</p>
|
||||
</div>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300 mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Standard membership for individuals 18+</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Full voting rights</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card p-6 border-primary">
|
||||
<div className="text-center mb-6">
|
||||
<span className="inline-block bg-primary text-white px-3 py-1 rounded-full text-sm font-medium mb-2">Most Popular</span>
|
||||
<h3 className="text-2xl font-bold mb-2">Family</h3>
|
||||
<p className="text-3xl font-bold text-primary">$45/year</p>
|
||||
</div>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300 mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Covers 2 adults and children under 18</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Two voting memberships</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Family-friendly event notifications</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="text-2xl font-bold mb-2">Lifetime</h3>
|
||||
<p className="text-3xl font-bold text-primary">$500</p>
|
||||
</div>
|
||||
<ul className="space-y-3 text-gray-600 dark:text-gray-300 mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>One-time payment for lifetime membership</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Full voting rights</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Recognition as lifetime supporter</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Note: Student and senior discounts are available. Please
|
||||
<Link href="/contact" className="text-primary hover:underline"> contact us </Link>
|
||||
for more information.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How to Join */}
|
||||
<section className="py-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">How to Join</h2>
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="bg-primary text-white flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Complete Application</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Contact us through the website or attend one of our events to get a membership application form.
|
||||
You can also download the form and submit it via email.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="bg-primary text-white flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Pay Membership Dues</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Submit your membership dues via check, cash, or online payment. Dues can be paid annually or as a one-time payment for lifetime membership.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="bg-primary text-white flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Board Approval</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
All applications are reviewed and approved by the OCD board. This process typically takes 1-2 weeks.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="bg-primary text-white flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl">
|
||||
4
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold mb-2">Receive Membership Card</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Once approved, you'll receive your official OCD membership card and welcome packet with information about upcoming events and activities.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 text-center">
|
||||
<Link
|
||||
href="/contact"
|
||||
className="btn-primary inline-block px-8 py-3 rounded-lg"
|
||||
>
|
||||
Request Application Form
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">What Our Members Say</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="card p-6">
|
||||
<blockquote className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
"Joining OCD was one of the best decisions I've made. It's helped me connect with the deaf community in Olathe and make lifelong friends."
|
||||
</blockquote>
|
||||
<div className="font-semibold">Sarah K.</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">Member since 2019</div>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<blockquote className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
"As a parent of a deaf child, OCD has been an invaluable resource for our family. The events and workshops have helped us all learn and grow together."
|
||||
</blockquote>
|
||||
<div className="font-semibold">David M.</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">Member since 2021</div>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<blockquote className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
"I love being part of such a vibrant and supportive community. The social events and activities have really enriched my life."
|
||||
</blockquote>
|
||||
<div className="font-semibold">Maria J.</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">Member since 2018</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-16 bg-primary text-white">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Ready to Join?</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Become a member today and connect with the deaf community in Olathe.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
href="/contact"
|
||||
className="bg-white text-primary hover:bg-gray-100 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Request Application
|
||||
</Link>
|
||||
<Link
|
||||
href="/events"
|
||||
className="bg-primary-dark text-white hover:bg-primary-light border border-white px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Attend an Event
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,97 +2,191 @@
|
|||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import Hero from '../components/molecules/Hero';
|
||||
|
||||
// Featured Event Card Component
|
||||
interface EventCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
imageUrl?: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const EventCard: React.FC<EventCardProps> = ({
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
imageUrl,
|
||||
slug
|
||||
}) => {
|
||||
return (
|
||||
<div className="card h-full flex flex-col">
|
||||
<div className="aspect-video bg-gray-200 relative">
|
||||
{imageUrl ? (
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<span className="text-gray-400">No image available</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-6 flex-grow flex flex-col">
|
||||
<h3 className="text-xl font-semibold mb-2">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4 flex-grow">{description}</p>
|
||||
<div className="flex justify-between items-center mt-auto">
|
||||
<span className="text-primary font-medium">{date}</span>
|
||||
<Link href={`/events/${slug}`} className="text-primary hover:underline">
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Skip to content link for accessibility */}
|
||||
<a href="#main-content" className="skip-to-content">
|
||||
Skip to content
|
||||
</a>
|
||||
<>
|
||||
{/* Hero Section */}
|
||||
<Hero
|
||||
title="Olathe Club of the Deaf"
|
||||
subtitle="A community organization serving the deaf and hard-of-hearing community in Olathe since 1975."
|
||||
primaryButtonText="Upcoming Events"
|
||||
primaryButtonLink="/events"
|
||||
secondaryButtonText="Join OCD"
|
||||
secondaryButtonLink="/membership"
|
||||
/>
|
||||
|
||||
{/* Hero section */}
|
||||
<section
|
||||
id="main-content"
|
||||
className="py-16 mb-12 text-center"
|
||||
>
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4">
|
||||
Olathe Club of the Deaf
|
||||
</h1>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
A community organization serving the deaf and hard-of-hearing community in Olathe since 1975.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<Link
|
||||
href="/events"
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-200"
|
||||
>
|
||||
Upcoming Events
|
||||
</Link>
|
||||
<Link
|
||||
href="/membership"
|
||||
className="bg-white hover:bg-gray-100 text-blue-600 border border-blue-600 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-200"
|
||||
>
|
||||
Join OCD
|
||||
</Link>
|
||||
{/* Featured events section */}
|
||||
<section className="py-16 bg-white dark:bg-gray-900">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Featured Events</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<EventCard
|
||||
title="Monthly Social"
|
||||
description="Join us for our monthly social gathering with games, refreshments, and great conversation."
|
||||
date="Apr 15, 2025"
|
||||
slug="monthly-social"
|
||||
/>
|
||||
<EventCard
|
||||
title="ASL Workshop"
|
||||
description="Learn new signs and practice your ASL skills with our certified instructors."
|
||||
date="Apr 22, 2025"
|
||||
slug="asl-workshop"
|
||||
/>
|
||||
<EventCard
|
||||
title="Board Meeting"
|
||||
description="Monthly board meeting open to all members. Come share your ideas and feedback."
|
||||
date="May 5, 2025"
|
||||
slug="board-meeting"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-10">
|
||||
<Link
|
||||
href="/events"
|
||||
className="btn-secondary inline-block"
|
||||
>
|
||||
View All Events
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured events section */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Featured Events</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{/* Event cards will be populated here */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden">
|
||||
<div className="h-48 bg-gray-200"></div>
|
||||
<div className="p-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Monthly Social</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">Join us for our monthly social gathering with games, refreshments, and great conversation.</p>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-blue-600 font-medium">Apr 15, 2025</span>
|
||||
<Link href="/events/monthly-social" className="text-blue-600 hover:underline">
|
||||
Learn more
|
||||
</Link>
|
||||
{/* About section with video */}
|
||||
<section className="py-16 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">About OCD</h2>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<p className="text-lg mb-6">
|
||||
Olathe Club of the Deaf (OCD) is a non-profit organization dedicated to serving the deaf and hard-of-hearing community in Olathe and surrounding areas.
|
||||
</p>
|
||||
<p className="text-lg mb-6">
|
||||
Our mission is to promote social, educational, and recreational opportunities for deaf individuals and their families while fostering a sense of community and belonging.
|
||||
</p>
|
||||
<p className="text-lg mb-6">
|
||||
Founded in 1975, we have a rich history of advocacy and community service, working to bridge the gap between the deaf and hearing communities.
|
||||
</p>
|
||||
<Link href="/about" className="btn-primary inline-block">
|
||||
Learn More About Us
|
||||
</Link>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-700 rounded-lg shadow-md p-2 aspect-video relative">
|
||||
<div className="w-full h-full flex items-center justify-center bg-gray-200 dark:bg-gray-600 rounded">
|
||||
<p className="text-gray-600 dark:text-gray-300">ASL Video Introduction</p>
|
||||
{/* Video player will be implemented here */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* About section with video */}
|
||||
<section className="mb-16">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">About OCD</h2>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
|
||||
<div>
|
||||
<p className="text-lg mb-4">
|
||||
Olathe Club of the Deaf (OCD) is a non-profit organization dedicated to serving the deaf and hard-of-hearing community in Olathe and surrounding areas.
|
||||
</p>
|
||||
<p className="text-lg mb-4">
|
||||
Our mission is to promote social, educational, and recreational opportunities for deaf individuals and their families while fostering a sense of community and belonging.
|
||||
</p>
|
||||
<Link href="/about" className="text-blue-600 hover:underline font-medium">
|
||||
Learn more about our history
|
||||
</Link>
|
||||
{/* Membership Benefits */}
|
||||
<section className="py-16 bg-white dark:bg-gray-900">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl font-bold mb-8 text-center">Membership Benefits</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Community Events</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Access to all OCD community events, socials, and gatherings throughout the year.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Voting Rights</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Have your say in club decisions and vote in board elections.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<h3 className="text-xl font-semibold mb-4">Support Network</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Connect with a supportive network of deaf and hard-of-hearing individuals.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-200 rounded-lg h-64 flex items-center justify-center">
|
||||
<p className="text-gray-600">Video introduction will go here</p>
|
||||
<div className="text-center mt-10">
|
||||
<Link
|
||||
href="/membership"
|
||||
className="btn-primary inline-block"
|
||||
>
|
||||
Become A Member
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Call to action */}
|
||||
<section className="bg-blue-600 text-white py-12 px-6 rounded-lg text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Join Our Community</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Become a member today and connect with the deaf community in Olathe.
|
||||
</p>
|
||||
<Link
|
||||
href="/membership"
|
||||
className="bg-white text-blue-600 hover:bg-gray-100 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-200"
|
||||
>
|
||||
Become a Member
|
||||
</Link>
|
||||
<section className="py-16 bg-primary text-white">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Join Our Community</h2>
|
||||
<p className="text-xl max-w-3xl mx-auto mb-8">
|
||||
Become a member today and connect with the deaf community in Olathe.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
href="/membership"
|
||||
className="bg-white text-primary hover:bg-gray-100 px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Become a Member
|
||||
</Link>
|
||||
<Link
|
||||
href="/donate"
|
||||
className="bg-primary-dark text-white hover:bg-primary-light border border-white px-6 py-3 rounded-lg font-medium text-lg transition-colors duration-300"
|
||||
>
|
||||
Support Our Mission
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
252
frontend/src/components/molecules/ContactForm.tsx
Normal file
252
frontend/src/components/molecules/ContactForm.tsx
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface FormErrors {
|
||||
name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const ContactForm: React.FC = () => {
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitSuccess, setSubmitSuccess] = useState(false);
|
||||
const [submitError, setSubmitError] = useState('');
|
||||
|
||||
const validateForm = (): boolean => {
|
||||
const newErrors: FormErrors = {};
|
||||
|
||||
// Name validation
|
||||
if (!formData.name.trim()) {
|
||||
newErrors.name = 'Name is required';
|
||||
}
|
||||
|
||||
// Email validation
|
||||
if (!formData.email.trim()) {
|
||||
newErrors.email = 'Email is required';
|
||||
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(formData.email)) {
|
||||
newErrors.email = 'Invalid email address';
|
||||
}
|
||||
|
||||
// Phone validation (optional but must be valid if provided)
|
||||
if (formData.phone && !/^[0-9]{3}[-. ]?[0-9]{3}[-. ]?[0-9]{4}$/.test(formData.phone)) {
|
||||
newErrors.phone = 'Invalid phone number format';
|
||||
}
|
||||
|
||||
// Message validation
|
||||
if (!formData.message.trim()) {
|
||||
newErrors.message = 'Message is required';
|
||||
} else if (formData.message.length < 10) {
|
||||
newErrors.message = 'Message must be at least 10 characters';
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({
|
||||
...formData,
|
||||
[name]: value,
|
||||
});
|
||||
|
||||
// Clear error when user types
|
||||
if (errors[name as keyof FormErrors]) {
|
||||
setErrors({
|
||||
...errors,
|
||||
[name]: undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
setSubmitError('');
|
||||
|
||||
try {
|
||||
// In a real implementation, we would send this to the backend API
|
||||
// For now, we'll just simulate a successful submission
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Clear form
|
||||
setFormData({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
setSubmitSuccess(true);
|
||||
setTimeout(() => setSubmitSuccess(false), 5000);
|
||||
} catch (error) {
|
||||
setSubmitError('There was a problem submitting your message. Please try again later.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="space-y-6"
|
||||
aria-label="Contact form"
|
||||
noValidate
|
||||
>
|
||||
{/* Success message */}
|
||||
{submitSuccess && (
|
||||
<div className="bg-green-100 dark:bg-green-900 border border-green-400 dark:border-green-600 text-green-700 dark:text-green-200 px-4 py-3 rounded" role="alert">
|
||||
<p className="font-medium">Thank you for your message!</p>
|
||||
<p>We'll get back to you as soon as possible.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error message */}
|
||||
{submitError && (
|
||||
<div className="bg-red-100 dark:bg-red-900 border border-red-400 dark:border-red-600 text-red-700 dark:text-red-200 px-4 py-3 rounded" role="alert">
|
||||
<p className="font-medium">Error</p>
|
||||
<p>{submitError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Name field */}
|
||||
<div>
|
||||
<label htmlFor="name" className="form-label">
|
||||
Name <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
aria-required="true"
|
||||
aria-invalid={!!errors.name}
|
||||
aria-describedby={errors.name ? 'name-error' : undefined}
|
||||
className={`form-input w-full ${errors.name ? 'border-red-500 dark:border-red-400' : ''}`}
|
||||
placeholder="Your name"
|
||||
/>
|
||||
{errors.name && (
|
||||
<p className="mt-1 text-red-600 dark:text-red-400 text-sm" id="name-error">
|
||||
{errors.name}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Email field */}
|
||||
<div>
|
||||
<label htmlFor="email" className="form-label">
|
||||
Email <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
aria-required="true"
|
||||
aria-invalid={!!errors.email}
|
||||
aria-describedby={errors.email ? 'email-error' : undefined}
|
||||
className={`form-input w-full ${errors.email ? 'border-red-500 dark:border-red-400' : ''}`}
|
||||
placeholder="your.email@example.com"
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="mt-1 text-red-600 dark:text-red-400 text-sm" id="email-error">
|
||||
{errors.email}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Phone field */}
|
||||
<div>
|
||||
<label htmlFor="phone" className="form-label">
|
||||
Phone (optional)
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
aria-invalid={!!errors.phone}
|
||||
aria-describedby={errors.phone ? 'phone-error' : undefined}
|
||||
className={`form-input w-full ${errors.phone ? 'border-red-500 dark:border-red-400' : ''}`}
|
||||
placeholder="123-456-7890"
|
||||
/>
|
||||
{errors.phone && (
|
||||
<p className="mt-1 text-red-600 dark:text-red-400 text-sm" id="phone-error">
|
||||
{errors.phone}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Message field */}
|
||||
<div>
|
||||
<label htmlFor="message" className="form-label">
|
||||
Message <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
rows={5}
|
||||
aria-required="true"
|
||||
aria-invalid={!!errors.message}
|
||||
aria-describedby={errors.message ? 'message-error' : undefined}
|
||||
className={`form-input w-full ${errors.message ? 'border-red-500 dark:border-red-400' : ''}`}
|
||||
placeholder="Your message"
|
||||
></textarea>
|
||||
{errors.message && (
|
||||
<p className="mt-1 text-red-600 dark:text-red-400 text-sm" id="message-error">
|
||||
{errors.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Submit button */}
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="btn-primary w-full py-3"
|
||||
aria-label={isSubmitting ? 'Submitting form' : 'Submit form'}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<span>Sending...</span>
|
||||
) : (
|
||||
<span>Send Message</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<span className="text-red-600">*</span> Required fields
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
120
frontend/src/components/molecules/Hero.tsx
Normal file
120
frontend/src/components/molecules/Hero.tsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface HeroProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
primaryButtonText: string;
|
||||
primaryButtonLink: string;
|
||||
secondaryButtonText?: string;
|
||||
secondaryButtonLink?: string;
|
||||
backgroundImageUrl?: string;
|
||||
}
|
||||
|
||||
const Hero: React.FC<HeroProps> = ({
|
||||
title,
|
||||
subtitle,
|
||||
primaryButtonText,
|
||||
primaryButtonLink,
|
||||
secondaryButtonText,
|
||||
secondaryButtonLink,
|
||||
backgroundImageUrl,
|
||||
}) => {
|
||||
// Animation variants
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
delayChildren: 0.1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { y: 20, opacity: 0 },
|
||||
visible: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.5,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Dynamic styles based on background image
|
||||
const bgStyle = backgroundImageUrl
|
||||
? {
|
||||
backgroundImage: `linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.7)), url(${backgroundImageUrl})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}
|
||||
: {};
|
||||
|
||||
// Default class for hero section (with or without bg image)
|
||||
const heroClass = backgroundImageUrl
|
||||
? 'text-white'
|
||||
: 'bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-indigo-950 text-gray-900 dark:text-white';
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`py-20 md:py-32 ${heroClass}`}
|
||||
style={bgStyle}
|
||||
aria-labelledby="hero-title"
|
||||
>
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<motion.div
|
||||
className="max-w-3xl mx-auto text-center"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
<motion.h1
|
||||
id="hero-title"
|
||||
className="text-4xl md:text-5xl lg:text-6xl font-bold mb-6"
|
||||
variants={itemVariants}
|
||||
>
|
||||
{title}
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
className={`text-xl md:text-2xl mb-8 ${
|
||||
backgroundImageUrl ? 'text-gray-200' : 'text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
variants={itemVariants}
|
||||
>
|
||||
{subtitle}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
className="flex flex-col sm:flex-row justify-center gap-4"
|
||||
variants={itemVariants}
|
||||
>
|
||||
<Link
|
||||
href={primaryButtonLink}
|
||||
className="btn-primary px-6 py-3 rounded-lg text-lg font-medium transition-colors duration-300 inline-block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
|
||||
>
|
||||
{primaryButtonText}
|
||||
</Link>
|
||||
|
||||
{secondaryButtonText && secondaryButtonLink && (
|
||||
<Link
|
||||
href={secondaryButtonLink}
|
||||
className="btn-secondary px-6 py-3 rounded-lg text-lg font-medium transition-colors duration-300 inline-block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
|
||||
>
|
||||
{secondaryButtonText}
|
||||
</Link>
|
||||
)}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
172
frontend/src/components/organisms/Footer.tsx
Normal file
172
frontend/src/components/organisms/Footer.tsx
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
// Icons
|
||||
const FacebookIcon = () => (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const InstagramIcon = () => (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const YouTubeIcon = () => (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M19.812 5.418c.861.23 1.538.907 1.768 1.768C21.998 8.746 22 12 22 12s0 3.255-.418 4.814a2.504 2.504 0 0 1-1.768 1.768c-1.56.419-7.814.419-7.814.419s-6.255 0-7.814-.419a2.505 2.505 0 0 1-1.768-1.768C2 15.255 2 12 2 12s0-3.255.417-4.814a2.507 2.507 0 0 1 1.768-1.768C5.744 5 11.998 5 11.998 5s6.255 0 7.814.418ZM15.194 12 10 15V9l5.194 3Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const Footer = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-800">
|
||||
<div className="container mx-auto px-4 py-12 lg:py-16">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
{/* Column 1: Logo and About */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-lg font-bold text-primary dark:text-primary-light">
|
||||
Olathe Club of the Deaf
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Serving the deaf community in Olathe since 1975.
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<a
|
||||
href="https://facebook.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="Facebook page"
|
||||
>
|
||||
<FacebookIcon />
|
||||
</a>
|
||||
<a
|
||||
href="https://instagram.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="Instagram profile"
|
||||
>
|
||||
<InstagramIcon />
|
||||
</a>
|
||||
<a
|
||||
href="https://youtube.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-500 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
aria-label="YouTube channel"
|
||||
>
|
||||
<YouTubeIcon />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Column 2: Main Navigation */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white uppercase tracking-wider mb-4">
|
||||
Navigation
|
||||
</h3>
|
||||
<nav className="flex flex-col space-y-2">
|
||||
<Link href="/" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Home
|
||||
</Link>
|
||||
<Link href="/about" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
About
|
||||
</Link>
|
||||
<Link href="/events" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Events
|
||||
</Link>
|
||||
<Link href="/membership" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Membership
|
||||
</Link>
|
||||
<Link href="/contact" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Contact
|
||||
</Link>
|
||||
<Link href="/donate" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Donate
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Column 3: About Links */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white uppercase tracking-wider mb-4">
|
||||
About OCD
|
||||
</h3>
|
||||
<nav className="flex flex-col space-y-2">
|
||||
<Link href="/about/history" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
History
|
||||
</Link>
|
||||
<Link href="/about/board" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Board Members
|
||||
</Link>
|
||||
<Link href="/about/bylaws" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Bylaws
|
||||
</Link>
|
||||
<Link href="/about/minutes" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Meeting Minutes
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Column 4: Accessibility */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white uppercase tracking-wider mb-4">
|
||||
Accessibility
|
||||
</h3>
|
||||
<nav className="flex flex-col space-y-2">
|
||||
<Link href="/accessibility" className="text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light">
|
||||
Accessibility Statement
|
||||
</Link>
|
||||
<button
|
||||
className="text-left text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
onClick={() => {
|
||||
// Toggle high contrast mode would go here
|
||||
alert('High contrast mode toggle would be implemented here');
|
||||
}}
|
||||
>
|
||||
High Contrast Mode
|
||||
</button>
|
||||
<button
|
||||
className="text-left text-gray-600 hover:text-primary dark:text-gray-400 dark:hover:text-primary-light"
|
||||
onClick={() => {
|
||||
// Toggle reduced motion would go here
|
||||
alert('Reduced motion toggle would be implemented here');
|
||||
}}
|
||||
>
|
||||
Reduced Motion
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Section with Copyright */}
|
||||
<div className="mt-12 pt-8 border-t border-gray-200 dark:border-gray-800">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 text-center">
|
||||
© {currentYear} Olathe Club of the Deaf. All rights reserved.
|
||||
</p>
|
||||
<div className="mt-4 flex flex-col sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-x-6 text-sm text-gray-500 dark:text-gray-400">
|
||||
<Link href="/privacy" className="hover:text-primary dark:hover:text-primary-light">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link href="/terms" className="hover:text-primary dark:hover:text-primary-light">
|
||||
Terms of Service
|
||||
</Link>
|
||||
<Link href="/sitemap" className="hover:text-primary dark:hover:text-primary-light">
|
||||
Sitemap
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
161
frontend/src/components/organisms/Navigation.tsx
Normal file
161
frontend/src/components/organisms/Navigation.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
// Icons
|
||||
const MenuIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={`w-6 h-6 ${className}`}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CloseIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={`w-6 h-6 ${className}`}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
interface NavLinkProps {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
isMobile?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const NavLink = ({ href, children, isMobile = false, onClick }: NavLinkProps) => {
|
||||
const pathname = usePathname();
|
||||
const isActive = pathname === href;
|
||||
|
||||
const baseStyles = "transition-colors duration-200 px-3 py-2 rounded-md text-lg font-medium";
|
||||
const mobileStyles = isMobile ? "block w-full text-left" : "";
|
||||
const activeStyles = isActive
|
||||
? "bg-primary-light text-white dark:bg-primary-dark"
|
||||
: "text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700";
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={`${baseStyles} ${mobileStyles} ${activeStyles}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const Navigation = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
const toggleMenu = () => setIsOpen(!isOpen);
|
||||
const closeMenu = () => setIsOpen(false);
|
||||
|
||||
// Handle scroll effect
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const offset = window.scrollY;
|
||||
if (offset > 50) {
|
||||
setScrolled(true);
|
||||
} else {
|
||||
setScrolled(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Close menu on ESC key
|
||||
useEffect(() => {
|
||||
const handleEsc = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleEsc);
|
||||
return () => window.removeEventListener('keydown', handleEsc);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`
|
||||
fixed top-0 z-40 w-full
|
||||
transition-all duration-300 ease-in-out
|
||||
${scrolled ? 'bg-white dark:bg-gray-900 shadow-md' : 'bg-transparent'}
|
||||
`}
|
||||
aria-label="Main Navigation"
|
||||
>
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between h-16 items-center">
|
||||
{/* Logo and site name */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link href="/" className="flex items-center" onClick={closeMenu}>
|
||||
<span className="text-xl font-bold text-primary dark:text-primary-light">Olathe Club of the Deaf</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop navigation */}
|
||||
<div className="hidden md:block">
|
||||
<div className="ml-10 flex items-center space-x-4">
|
||||
<NavLink href="/">Home</NavLink>
|
||||
<NavLink href="/about">About</NavLink>
|
||||
<NavLink href="/events">Events</NavLink>
|
||||
<NavLink href="/membership">Membership</NavLink>
|
||||
<NavLink href="/contact">Contact</NavLink>
|
||||
<NavLink href="/donate">Donate</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<div className="md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:text-gray-900 hover:bg-gray-100 dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary"
|
||||
aria-controls="mobile-menu"
|
||||
aria-expanded={isOpen}
|
||||
onClick={toggleMenu}
|
||||
>
|
||||
<span className="sr-only">{isOpen ? 'Close main menu' : 'Open main menu'}</span>
|
||||
{isOpen ? <CloseIcon /> : <MenuIcon />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu, show/hide based on menu state */}
|
||||
<div
|
||||
className={`md:hidden ${isOpen ? 'block' : 'hidden'}`}
|
||||
id="mobile-menu"
|
||||
>
|
||||
<div className="px-2 pt-2 pb-3 space-y-1 bg-white dark:bg-gray-900 shadow-lg">
|
||||
<NavLink href="/" isMobile onClick={closeMenu}>Home</NavLink>
|
||||
<NavLink href="/about" isMobile onClick={closeMenu}>About</NavLink>
|
||||
<NavLink href="/events" isMobile onClick={closeMenu}>Events</NavLink>
|
||||
<NavLink href="/membership" isMobile onClick={closeMenu}>Membership</NavLink>
|
||||
<NavLink href="/contact" isMobile onClick={closeMenu}>Contact</NavLink>
|
||||
<NavLink href="/donate" isMobile onClick={closeMenu}>Donate</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
104
frontend/src/styles/tailwind.css
Normal file
104
frontend/src/styles/tailwind.css
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
/* CSS-first approach with @theme directives */
|
||||
@theme {
|
||||
--color-primary: #1a56db;
|
||||
--color-primary-light: #3b82f6;
|
||||
--color-primary-dark: #1e40af;
|
||||
|
||||
--color-secondary: #7e3af2;
|
||||
--color-secondary-light: #a78bfa;
|
||||
--color-secondary-dark: #5b21b6;
|
||||
|
||||
--color-accent: #ff9800;
|
||||
--color-accent-light: #ffca28;
|
||||
--color-accent-dark: #ff8f00;
|
||||
|
||||
--color-text: #171717;
|
||||
--color-text-light: #6b7280;
|
||||
|
||||
--font-sans: "Inter", system-ui, sans-serif;
|
||||
--font-display: "Inter", system-ui, sans-serif;
|
||||
|
||||
--border-radius-sm: 0.25rem;
|
||||
--border-radius: 0.5rem;
|
||||
--border-radius-lg: 0.75rem;
|
||||
--border-radius-xl: 1rem;
|
||||
|
||||
--shadow-focus: 0 0 0 3px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
@apply scroll-smooth text-base;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100;
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply font-bold;
|
||||
font-family: var(--font-display);
|
||||
}
|
||||
|
||||
/* Focus styles for accessibility */
|
||||
*:focus-visible {
|
||||
@apply outline-none;
|
||||
box-shadow: var(--shadow-focus);
|
||||
}
|
||||
|
||||
/* Skip to content link - accessibility */
|
||||
.skip-to-content {
|
||||
@apply sr-only focus:not-sr-only focus:absolute focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-white;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.container {
|
||||
@apply mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-primary transition-colors;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-primary text-white hover:bg-primary-dark;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-white text-primary border border-primary hover:bg-gray-50;
|
||||
}
|
||||
|
||||
.btn-accent {
|
||||
@apply bg-accent text-white hover:bg-accent-dark;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
@apply block w-full rounded-md border-gray-300 shadow-sm focus:border-primary focus:ring-primary sm:text-sm;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
@apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode adjustments */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-text: #f3f4f6;
|
||||
--color-text-light: #d1d5db;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +1,81 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
export default {
|
||||
content: [
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#1a56db',
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: '#7e3af2',
|
||||
50: '#f5f3ff',
|
||||
100: '#ede9fe',
|
||||
200: '#ddd6fe',
|
||||
300: '#c4b5fd',
|
||||
400: '#a78bfa',
|
||||
500: '#8b5cf6',
|
||||
600: '#7c3aed',
|
||||
700: '#6d28d9',
|
||||
800: '#5b21b6',
|
||||
900: '#4c1d95',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: '#ff9800',
|
||||
50: '#fff8e1',
|
||||
100: '#ffecb3',
|
||||
200: '#ffe082',
|
||||
300: '#ffd54f',
|
||||
400: '#ffca28',
|
||||
500: '#ffc107',
|
||||
600: '#ffb300',
|
||||
700: '#ffa000',
|
||||
800: '#ff8f00',
|
||||
900: '#ff6f00',
|
||||
},
|
||||
colors: {
|
||||
'primary': 'var(--color-primary)',
|
||||
'primary-light': 'var(--color-primary-light)',
|
||||
'primary-dark': 'var(--color-primary-dark)',
|
||||
'secondary': 'var(--color-secondary)',
|
||||
'secondary-light': 'var(--color-secondary-light)',
|
||||
'secondary-dark': 'var(--color-secondary-dark)',
|
||||
'accent': 'var(--color-accent)',
|
||||
'accent-light': 'var(--color-accent-light)',
|
||||
'accent-dark': 'var(--color-accent-dark)',
|
||||
'transparent': 'transparent',
|
||||
'current': 'currentColor',
|
||||
'black': '#000',
|
||||
'white': '#fff',
|
||||
'gray': {
|
||||
50: '#f9fafb',
|
||||
100: '#f3f4f6',
|
||||
200: '#e5e7eb',
|
||||
300: '#d1d5db',
|
||||
400: '#9ca3af',
|
||||
500: '#6b7280',
|
||||
600: '#4b5563',
|
||||
700: '#374151',
|
||||
800: '#1f2937',
|
||||
900: '#111827',
|
||||
950: '#030712',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
'blue': {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
},
|
||||
borderRadius: {
|
||||
'xl': '0.75rem',
|
||||
'2xl': '1rem',
|
||||
'indigo': {
|
||||
50: '#eef2ff',
|
||||
100: '#e0e7ff',
|
||||
900: '#312e81',
|
||||
950: '#1e1b4b',
|
||||
},
|
||||
boxShadow: {
|
||||
'focus': '0 0 0 3px rgba(59, 130, 246, 0.5)',
|
||||
'red': {
|
||||
100: '#fee2e2',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
900: '#7f1d1d',
|
||||
},
|
||||
typography: (theme) => ({
|
||||
DEFAULT: {
|
||||
css: {
|
||||
a: {
|
||||
color: theme('colors.primary.DEFAULT'),
|
||||
'&:hover': {
|
||||
color: theme('colors.primary.700'),
|
||||
},
|
||||
},
|
||||
h1: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
h2: {
|
||||
fontWeight: '600',
|
||||
},
|
||||
h3: {
|
||||
fontWeight: '600',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
'green': {
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
900: '#14532d',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
'sans': 'var(--font-sans)',
|
||||
'display': 'var(--font-display)',
|
||||
},
|
||||
borderRadius: {
|
||||
'none': '0px',
|
||||
'sm': 'var(--border-radius-sm)',
|
||||
'DEFAULT': 'var(--border-radius)',
|
||||
'md': 'var(--border-radius)',
|
||||
'lg': 'var(--border-radius-lg)',
|
||||
'xl': 'var(--border-radius-xl)',
|
||||
'full': '9999px',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
darkMode: 'media', // 'media' or 'class'
|
||||
future: {
|
||||
hoverOnlyWhenSupported: true,
|
||||
},
|
||||
darkMode: 'media',
|
||||
plugins: [],
|
||||
};
|
||||
|
|
|
|||
35
frontend/tsconfig.json
Normal file
35
frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"module": "esnext",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
".next/types/**/*.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue