- Created complete project directory structure: - Frontend with Next.js 15.2.3 app router structure - Backend with Express API endpoints - Docker configurations with security best practices - MongoDB initialization with schema validation - Nginx configuration with security headers - Implemented core file templates: - Custom video player with transcript display options - MongoDB schemas with validation for Videos and Members - JWT authentication middleware with role-based access control - Docker Compose with resource limits and security - Frontend/backend package.json with dependencies - Updated documentation: - Enhanced TECH_STACK with latest version details - Updated TO_DO.txt with WCAG 2.2 AA requirements - Updated planned_implementation.txt with current best practices - Refreshed all Memory Bank files with current state - Security and accessibility improvements: - Added MongoDB 7.0 schema validation with - Enhanced video requirements with mandatory transcripts and thumbnails - Updated accessibility to WCAG 2.2 AA standards - Added security best practices for Docker deployments
1036 lines
30 KiB
Text
1036 lines
30 KiB
Text
# Olathe Club of the Deaf (OCD) Website: Comprehensive Implementation Plan
|
||
|
||
## Project Overview
|
||
|
||
The Olathe Club of the Deaf (OCD) website will serve as a digital hub for the deaf community in Olathe, providing accessible information, event management, membership tracking, and administrative capabilities. This implementation plan outlines the detailed technical approach, architecture, development phases, and timelines.
|
||
|
||
## Technical Architecture
|
||
|
||
### Frontend Architecture
|
||
- **Framework**: Next.js 15.2.3 with React 19
|
||
- **Rendering Strategy**:
|
||
- Server-side rendering for critical pages (home, about, events)
|
||
- Static generation for stable content (bylaws, history)
|
||
- Client-side rendering for interactive components (calendar, forms)
|
||
- **State Management**: React Context for application state
|
||
- **Styling**:
|
||
- Tailwind CSS 4.0 for responsive design with CSS-first configuration
|
||
- PostCSS 8.4 with Autoprefixer 10.4
|
||
- Native CSS variables for theme colors and 3D transforms
|
||
- **Animation**: Framer Motion 11 for UI transitions
|
||
- **Routing**: Next.js App Router with nested layouts
|
||
- **Interactive Components**:
|
||
- React Simple Maps 4.0 for location visualization
|
||
- Custom video player with WebVTT support
|
||
- React Intersection Observer 9.13 for scroll animations
|
||
|
||
### Backend Architecture
|
||
- **Framework**: Node.js with Express 4.21
|
||
- **API Design**: RESTful endpoints
|
||
- **Authentication**:
|
||
- JWT-based authentication for admin access
|
||
- HTTP-only cookies for session management
|
||
- **Data Layer**:
|
||
- MongoDB for document storage
|
||
- Mongoose for schema validation and ODM
|
||
- Redis for caching and session management
|
||
- **Email**: Nodemailer 6.9 with Gmail SMTP
|
||
- **Performance**:
|
||
- Redis-backed rate limiting
|
||
- Response compression
|
||
- Efficient database queries with indexing
|
||
|
||
### Database Schema with Validation
|
||
1. **Admin User**
|
||
```javascript
|
||
db.createCollection("adminUsers", {
|
||
validator: {
|
||
$jsonSchema: {
|
||
bsonType: "object",
|
||
required: ["username", "passwordHash", "email"],
|
||
properties: {
|
||
_id: { bsonType: "objectId" },
|
||
username: { bsonType: "string", minLength: 3, maxLength: 50 },
|
||
passwordHash: { bsonType: "string" },
|
||
email: {
|
||
bsonType: "string",
|
||
pattern: "^.+@.+\\..+$"
|
||
},
|
||
lastLogin: { bsonType: "date" },
|
||
resetToken: { bsonType: "string" },
|
||
resetTokenExpiry: { bsonType: "date" }
|
||
}
|
||
}
|
||
},
|
||
validationLevel: "strict"
|
||
});
|
||
|
||
// Create indexes for security and performance
|
||
db.adminUsers.createIndex({ "username": 1 }, { unique: true });
|
||
db.adminUsers.createIndex({ "email": 1 }, { unique: true });
|
||
db.adminUsers.createIndex({ "resetToken": 1 }, { sparse: true });
|
||
```
|
||
|
||
2. **Member**
|
||
```javascript
|
||
{
|
||
_id: ObjectId,
|
||
firstName: String,
|
||
lastName: String,
|
||
email: String,
|
||
phone: String,
|
||
address: {
|
||
street: String,
|
||
city: String,
|
||
state: String,
|
||
zip: String
|
||
},
|
||
membershipType: String, // "regular", "lifetime", "honorary"
|
||
joinDate: Date,
|
||
expirationDate: Date,
|
||
status: String, // "active", "expired", "pending"
|
||
notificationPreference: String, // "email", "sms", "both"
|
||
lastRenewalDate: Date,
|
||
boardMember: Boolean,
|
||
boardPosition: String, // if applicable
|
||
emergencyContact: {
|
||
name: String,
|
||
relationship: String,
|
||
phone: String
|
||
}
|
||
}
|
||
```
|
||
|
||
3. **Event**
|
||
```javascript
|
||
{
|
||
_id: ObjectId,
|
||
title: String,
|
||
description: String,
|
||
startDate: Date,
|
||
endDate: Date,
|
||
location: {
|
||
name: String,
|
||
address: String,
|
||
coordinates: {
|
||
lat: Number,
|
||
lng: Number
|
||
}
|
||
},
|
||
category: String, // "social", "athletic", "board", "general"
|
||
image: String, // URL to image
|
||
recurring: Boolean,
|
||
recurrencePattern: String, // "weekly", "monthly", "annual"
|
||
createdAt: Date,
|
||
updatedAt: Date,
|
||
isPublished: Boolean,
|
||
registrationRequired: Boolean,
|
||
registeredMembers: [ObjectId], // References to Member
|
||
featuredOnHomepage: Boolean
|
||
}
|
||
```
|
||
|
||
4. **Page Content**
|
||
```javascript
|
||
{
|
||
_id: ObjectId,
|
||
slug: String, // URL path
|
||
title: String,
|
||
content: String, // Rich text HTML
|
||
metaDescription: String,
|
||
lastUpdated: Date,
|
||
createdBy: ObjectId, // Reference to Admin
|
||
publishStatus: String, // "draft", "published"
|
||
sections: [
|
||
{
|
||
id: String,
|
||
title: String,
|
||
content: String,
|
||
order: Number
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
5. **Document**
|
||
```javascript
|
||
{
|
||
_id: ObjectId,
|
||
title: String,
|
||
description: String,
|
||
category: String, // "minutes", "bylaws", "forms"
|
||
fileUrl: String,
|
||
mimeType: String,
|
||
fileSize: Number,
|
||
uploadDate: Date,
|
||
lastModified: Date,
|
||
isPublic: Boolean,
|
||
tags: [String]
|
||
}
|
||
```
|
||
|
||
6. **Video**
|
||
```javascript
|
||
db.createCollection("videos", {
|
||
validator: {
|
||
$jsonSchema: {
|
||
bsonType: "object",
|
||
required: ["title", "fileUrl", "thumbnailUrl", "transcriptText"],
|
||
properties: {
|
||
_id: { bsonType: "objectId" },
|
||
title: { bsonType: "string", minLength: 3 },
|
||
description: { bsonType: "string" },
|
||
fileUrl: { bsonType: "string" },
|
||
thumbnailUrl: { bsonType: "string" }, // Required thumbnail preview image
|
||
duration: { bsonType: "number" }, // in seconds
|
||
uploadDate: { bsonType: "date" },
|
||
category: { bsonType: "string" },
|
||
subtitleUrl: { bsonType: "string" }, // WebVTT file URL
|
||
transcriptText: { bsonType: "string" }, // Required full text transcript
|
||
transcriptFormat: { bsonType: "string", enum: ["plain", "html", "json"] },
|
||
isPublished: { bsonType: "bool" },
|
||
relatedPageId: { bsonType: "objectId" }, // Reference to Page Content
|
||
tags: {
|
||
bsonType: "array",
|
||
items: { bsonType: "string" }
|
||
}
|
||
}
|
||
}
|
||
},
|
||
validationLevel: "strict"
|
||
});
|
||
|
||
// Create indexes for performance
|
||
db.videos.createIndex({ "title": "text", "transcriptText": "text" }); // Full-text search
|
||
db.videos.createIndex({ "category": 1, "uploadDate": -1 }); // Category browsing
|
||
db.videos.createIndex({ "tags": 1 }); // Tag filtering
|
||
```
|
||
|
||
7. **Contact Submission**
|
||
```javascript
|
||
{
|
||
_id: ObjectId,
|
||
name: String,
|
||
email: String,
|
||
phone: String,
|
||
message: String,
|
||
submissionDate: Date,
|
||
isRead: Boolean,
|
||
status: String, // "pending", "responded", "closed"
|
||
responseText: String,
|
||
responseDate: Date
|
||
}
|
||
```
|
||
|
||
### API Endpoints
|
||
|
||
#### Authentication
|
||
- `POST /api/auth/login` - Admin login
|
||
- `POST /api/auth/logout` - Admin logout
|
||
- `POST /api/auth/reset-password` - Password reset request
|
||
- `POST /api/auth/set-password` - Set new password
|
||
|
||
#### Content Management
|
||
- `GET /api/pages` - List all pages
|
||
- `GET /api/pages/:slug` - Get page by slug
|
||
- `POST /api/pages` - Create new page
|
||
- `PUT /api/pages/:id` - Update page
|
||
- `DELETE /api/pages/:id` - Delete page
|
||
- `PUT /api/pages/:id/publish` - Publish/unpublish page
|
||
|
||
#### Events
|
||
- `GET /api/events` - List events with filtering
|
||
- `GET /api/events/:id` - Get event details
|
||
- `POST /api/events` - Create event
|
||
- `PUT /api/events/:id` - Update event
|
||
- `DELETE /api/events/:id` - Delete event
|
||
- `GET /api/events/calendar/:year/:month` - Get calendar data
|
||
|
||
#### Members
|
||
- `GET /api/members` - List members (admin only)
|
||
- `GET /api/members/:id` - Get member details (admin only)
|
||
- `POST /api/members` - Add member (admin only)
|
||
- `PUT /api/members/:id` - Update member (admin only)
|
||
- `DELETE /api/members/:id` - Delete member (admin only)
|
||
- `GET /api/members/expiring` - Get soon-to-expire memberships
|
||
- `POST /api/members/notify` - Send notifications to members
|
||
|
||
#### Documents
|
||
- `GET /api/documents` - List documents with filtering
|
||
- `GET /api/documents/:id` - Get document details
|
||
- `POST /api/documents` - Upload document
|
||
- `PUT /api/documents/:id` - Update document metadata
|
||
- `DELETE /api/documents/:id` - Delete document
|
||
|
||
#### Videos
|
||
- `GET /api/videos` - List videos
|
||
- `GET /api/videos/:id` - Get video details
|
||
- `POST /api/videos` - Upload video
|
||
- `PUT /api/videos/:id` - Update video metadata
|
||
- `DELETE /api/videos/:id` - Delete video
|
||
- `POST /api/videos/:id/subtitles` - Upload subtitle file
|
||
|
||
#### Contact
|
||
- `POST /api/contact` - Submit contact form
|
||
- `GET /api/contact` - List contact submissions (admin only)
|
||
- `PUT /api/contact/:id` - Update contact submission status
|
||
- `POST /api/contact/:id/respond` - Respond to contact submission
|
||
|
||
## Development Phases & Timeline
|
||
|
||
### Phase 1: Project Setup & Infrastructure (Weeks 1-2)
|
||
- [ ] Initialize Next.js project with TypeScript
|
||
- [ ] Configure project structure (app directory, API routes)
|
||
- [ ] Set up ESLint with accessibility rules
|
||
- [ ] Create basic layout components (Header, Footer, Navigation)
|
||
- [ ] Implement responsive design breakpoints
|
||
- [ ] Configure MongoDB connection
|
||
- [ ] Set up Redis for caching
|
||
- [ ] Implement Docker development environment
|
||
- [ ] Create authentication middleware
|
||
- [ ] Configure admin login system
|
||
- [ ] Set up email service connection
|
||
|
||
#### Deliverables:
|
||
- Project repository with initial commit
|
||
- Development environment documentation
|
||
- Basic site structure with placeholder pages
|
||
- Admin authentication system
|
||
- Database connection and schema documentation
|
||
|
||
### Phase 2: Core Public Pages (Weeks 3-5)
|
||
- [ ] Implement home page with featured events section
|
||
- [ ] Create About OCD section pages
|
||
- [ ] History page
|
||
- [ ] Mission statement page
|
||
- [ ] Board members page with photo grid
|
||
- [ ] Implement bylaws page with document viewer
|
||
- [ ] Create minutes archive with filtering and search
|
||
- [ ] Develop responsive navigation system
|
||
- [ ] Implement SEO optimization
|
||
- [ ] Dynamic meta tags
|
||
- [ ] Structured data for events
|
||
- [ ] Sitemap generation
|
||
- [ ] Create contact form with validation and email notifications
|
||
- [ ] Implement breadcrumb navigation
|
||
|
||
#### Deliverables:
|
||
- Fully functional public-facing pages
|
||
- Responsive navigation system
|
||
- SEO implementation
|
||
- Contact form with notification system
|
||
- Document viewing capabilities
|
||
|
||
### Phase 3: Event Management & Calendar (Weeks 6-7)
|
||
- [ ] Implement event data model and API endpoints
|
||
- [ ] Create calendar visualization
|
||
- [ ] Monthly view
|
||
- [ ] List view
|
||
- [ ] Filtering capabilities
|
||
- [ ] Develop event detail pages
|
||
- [ ] Implement recurring event functionality
|
||
- [ ] Create event management interface for admin
|
||
- [ ] Implement featured events selection for homepage
|
||
- [ ] Develop event category system
|
||
|
||
#### Deliverables:
|
||
- Interactive calendar with multiple views
|
||
- Event detail pages
|
||
- Admin event management interface
|
||
- Event categorization system
|
||
|
||
### Phase 4: Membership System (Weeks 8-9)
|
||
- [ ] Implement member data model and API endpoints
|
||
- [ ] Create membership information pages
|
||
- [ ] Develop membership application form
|
||
- [ ] Implement membership tracking system
|
||
- [ ] Expiration dates
|
||
- [ ] Status tracking
|
||
- [ ] Renewal notifications
|
||
- [ ] Create member management interface for admin
|
||
- [ ] Implement automated email notifications
|
||
- [ ] Develop member directory (admin-only view)
|
||
- [ ] Create exportable member lists
|
||
|
||
#### Deliverables:
|
||
- Membership information pages
|
||
- Online application form
|
||
- Admin membership management interface
|
||
- Automated expiration notifications
|
||
- Member directory with export capabilities
|
||
|
||
### Phase 5: Admin Dashboard (Weeks 10-12)
|
||
- [ ] Create dashboard layout and navigation
|
||
- [ ] Implement WYSIWYG editor for content management
|
||
- [ ] Develop document repository management
|
||
- [ ] Upload interface
|
||
- [ ] Categorization
|
||
- [ ] Search functionality
|
||
- [ ] Create image management system
|
||
- [ ] Implement user analytics dashboard
|
||
- [ ] Develop contact submission management
|
||
- [ ] Create admin notification center
|
||
- [ ] Implement dashboard customization
|
||
|
||
#### Deliverables:
|
||
- Complete admin dashboard
|
||
- Content management system
|
||
- Document repository
|
||
- Analytics dashboard
|
||
- Contact management system
|
||
|
||
### Phase 6: Video & Accessibility Features (Weeks 13-14)
|
||
- [ ] Implement custom video player with accessibility features
|
||
- [ ] WebVTT subtitle support
|
||
- [ ] Playback speed controls
|
||
- [ ] Keyboard shortcuts
|
||
- [ ] Create video management interface
|
||
- [ ] Implement transcript display alongside videos
|
||
- [ ] Develop thumbnail generation system
|
||
- [ ] Create video category organization
|
||
- [ ] Implement comprehensive ARIA attributes
|
||
- [ ] Test with screen readers
|
||
- [ ] Implement high-contrast mode
|
||
|
||
#### Deliverables:
|
||
- Accessible custom video player
|
||
- Video management system
|
||
- Screen reader compatibility
|
||
- Accessibility compliance documentation
|
||
|
||
### Phase 7: Integration & Donation Features (Weeks 15-16)
|
||
- [ ] Implement Zeffy donation widget
|
||
- [ ] Create donation information page
|
||
- [ ] Integrate social media sharing
|
||
- [ ] Implement Google Maps for location
|
||
- [ ] Create social media preview cards
|
||
- [ ] Develop printable content functionality
|
||
- [ ] Implement newsletter signup form
|
||
|
||
#### Deliverables:
|
||
- Donation system integration
|
||
- Social media integration
|
||
- Location mapping
|
||
- Newsletter signup functionality
|
||
|
||
### Phase 8: Testing & Optimization (Weeks 17-18)
|
||
- [ ] Conduct comprehensive user testing
|
||
- [ ] Perform accessibility audit
|
||
- [ ] Implement performance optimizations
|
||
- [ ] Image optimization
|
||
- [ ] Code splitting
|
||
- [ ] Bundle size reduction
|
||
- [ ] Conduct security audit
|
||
- [ ] Fix bugs and refine UI/UX
|
||
- [ ] Optimize database queries
|
||
- [ ] Implement caching strategies
|
||
|
||
#### Deliverables:
|
||
- User testing report
|
||
- Accessibility audit report
|
||
- Performance optimization documentation
|
||
- Security audit report
|
||
- Finalized application with bugfixes
|
||
|
||
### Phase 9: Deployment & Documentation (Weeks 19-20)
|
||
- [ ] Set up production Docker environment
|
||
- [ ] Configure Nginx and SSL
|
||
- [ ] Implement automated backups
|
||
- [ ] Create deployment documentation
|
||
- [ ] Develop admin user guide
|
||
- [ ] Create technical documentation
|
||
- [ ] Implement monitoring and alerting
|
||
- [ ] Conduct training session for administrators
|
||
|
||
#### Deliverables:
|
||
- Production-ready deployment
|
||
- Comprehensive documentation
|
||
- Admin user guide
|
||
- Training materials
|
||
- Monitoring system
|
||
|
||
## Technical Implementation Details
|
||
|
||
### Responsive Design Implementation
|
||
- Mobile-first approach with breakpoints:
|
||
- Mobile: < 640px
|
||
- Tablet: 640px - 1023px
|
||
- Desktop: ≥ 1024px
|
||
- Fluid typography using CSS clamp()
|
||
- Flexible grid layouts with CSS Grid and Flexbox
|
||
- Component-based media queries using Tailwind's screen variants
|
||
|
||
### Accessibility Implementation
|
||
- WCAG 2.2 AA compliance requirements:
|
||
- Semantic HTML structure
|
||
- Keyboard navigation support
|
||
- Focus Not Obscured (2.4.11) - ensure focus indicators are clearly visible
|
||
- Dragging Movements (2.5.7) - provide alternatives for drag operations
|
||
- Accessible Authentication (3.3.7/3.3.8) - offer alternatives to cognitive tests
|
||
- Color contrast ratio ≥ 4.5:1
|
||
- Text resizing support without breaking layouts
|
||
- Target Size (2.5.8) - ensure touch targets are at least 24px × 24px
|
||
- Screen reader considerations:
|
||
- ARIA landmarks and labels
|
||
- Skip navigation links
|
||
- Descriptive alt text for images
|
||
- Status announcements with aria-live regions for dynamic content
|
||
- Mobile ARIA optimization
|
||
- DeafBlind user support:
|
||
- High contrast mode with adequate contrast ratios
|
||
- Text-only content alternatives
|
||
- Simple navigation patterns
|
||
- Reduced motion option
|
||
- Refreshable braille display compatibility with proper ARIA landmarks
|
||
|
||
### Video Player Implementation
|
||
```typescript
|
||
// Custom video player component
|
||
import { useState, useRef, useEffect } from 'react';
|
||
|
||
const VideoPlayer = ({
|
||
videoUrl,
|
||
subtitleUrl,
|
||
thumbnailUrl,
|
||
title
|
||
}) => {
|
||
const videoRef = useRef<HTMLVideoElement>(null);
|
||
const [isPlaying, setIsPlaying] = useState(false);
|
||
const [progress, setProgress] = useState(0);
|
||
const [currentTime, setCurrentTime] = useState(0);
|
||
const [duration, setDuration] = useState(0);
|
||
const [showSubtitles, setShowSubtitles] = useState(true);
|
||
const [playbackRate, setPlaybackRate] = useState(1);
|
||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||
|
||
// Implementation for controls (play/pause, seeking, etc.)
|
||
// Keyboard shortcut handling
|
||
// Fullscreen toggle
|
||
// Subtitle display logic
|
||
// Progress tracking
|
||
|
||
return (
|
||
<div className="video-player-container" aria-label={`Video player: ${title}`}>
|
||
<video
|
||
ref={videoRef}
|
||
src={videoUrl}
|
||
poster={thumbnailUrl}
|
||
className="video-element"
|
||
onClick={togglePlayPause}
|
||
onTimeUpdate={updateProgress}
|
||
onLoadedMetadata={initializeVideo}
|
||
>
|
||
{subtitleUrl && showSubtitles && (
|
||
<track
|
||
kind="subtitles"
|
||
src={subtitleUrl}
|
||
srcLang="en"
|
||
label="English"
|
||
default
|
||
/>
|
||
)}
|
||
</video>
|
||
|
||
{/* Custom control bar */}
|
||
<div className="controls-container">
|
||
<button
|
||
onClick={togglePlayPause}
|
||
aria-label={isPlaying ? "Pause" : "Play"}
|
||
>
|
||
{isPlaying ? "Pause" : "Play"}
|
||
</button>
|
||
|
||
{/* Progress bar */}
|
||
<div
|
||
className="progress-container"
|
||
onClick={seekToPosition}
|
||
role="slider"
|
||
aria-label="Video progress"
|
||
aria-valuemin={0}
|
||
aria-valuemax={100}
|
||
aria-valuenow={progress}
|
||
>
|
||
<div
|
||
className="progress-bar"
|
||
style={{ width: `${progress}%` }}
|
||
/>
|
||
</div>
|
||
|
||
{/* Additional controls */}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default VideoPlayer;
|
||
```
|
||
|
||
### Membership Expiration Notification System
|
||
```typescript
|
||
// Automated notification system
|
||
import { getAllMembers } from '../models/Member';
|
||
import { sendEmail } from '../utils/email';
|
||
|
||
async function checkExpiringMemberships() {
|
||
const today = new Date();
|
||
const thirtyDaysFromNow = new Date();
|
||
thirtyDaysFromNow.setDate(today.getDate() + 30);
|
||
|
||
// Get members expiring in the next 30 days
|
||
const expiringMembers = await getAllMembers({
|
||
expirationDate: {
|
||
$gte: today,
|
||
$lte: thirtyDaysFromNow
|
||
},
|
||
status: 'active'
|
||
});
|
||
|
||
// Send notifications to each member
|
||
for (const member of expiringMembers) {
|
||
await sendEmail({
|
||
to: member.email,
|
||
subject: 'Your OCD Membership is Expiring Soon',
|
||
template: 'membership-expiration',
|
||
data: {
|
||
firstName: member.firstName,
|
||
expirationDate: member.expirationDate,
|
||
renewalLink: `https://olathedeafclub.com/membership/renew?id=${member._id}`
|
||
}
|
||
});
|
||
|
||
// Update member notification status
|
||
await updateMemberNotificationSent(member._id, 'expiration-warning');
|
||
}
|
||
|
||
// Notify board about expiring members
|
||
await sendBoardNotification({
|
||
subject: 'Upcoming Membership Expirations',
|
||
data: {
|
||
expiringMembers: expiringMembers.map(m => ({
|
||
name: `${m.firstName} ${m.lastName}`,
|
||
expirationDate: m.expirationDate
|
||
}))
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### Calendar Component Implementation
|
||
```typescript
|
||
// Month view calendar component
|
||
import { useState, useEffect } from 'react';
|
||
import { fetchEventsForMonth } from '../api/events';
|
||
|
||
const Calendar = ({ year, month }) => {
|
||
const [events, setEvents] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [calendarDays, setCalendarDays] = useState([]);
|
||
|
||
useEffect(() => {
|
||
async function loadEvents() {
|
||
setLoading(true);
|
||
try {
|
||
const eventsData = await fetchEventsForMonth(year, month);
|
||
setEvents(eventsData);
|
||
generateCalendarDays(year, month, eventsData);
|
||
} catch (error) {
|
||
console.error('Error loading events:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
loadEvents();
|
||
}, [year, month]);
|
||
|
||
function generateCalendarDays(year, month, events) {
|
||
// Logic to generate calendar grid with events
|
||
// Handle month start/end dates
|
||
// Map events to specific days
|
||
// Calculate appropriate rows/columns
|
||
}
|
||
|
||
return (
|
||
<div className="calendar-container" aria-label={`Calendar for ${getMonthName(month)} ${year}`}>
|
||
{/* Calendar header with month/year and navigation */}
|
||
<div className="calendar-header">
|
||
<button onClick={() => navigateMonth(-1)} aria-label="Previous month">
|
||
←
|
||
</button>
|
||
<h2>{getMonthName(month)} {year}</h2>
|
||
<button onClick={() => navigateMonth(1)} aria-label="Next month">
|
||
→
|
||
</button>
|
||
</div>
|
||
|
||
{/* Calendar grid */}
|
||
<div className="calendar-grid" role="grid">
|
||
{/* Day of week headers */}
|
||
<div className="calendar-weekdays" role="row">
|
||
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
|
||
<div key={day} role="columnheader">{day}</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Calendar days */}
|
||
<div className="calendar-days">
|
||
{calendarDays.map(day => (
|
||
<div
|
||
key={day.date}
|
||
className={`calendar-day ${day.isCurrentMonth ? '' : 'other-month'}`}
|
||
role="gridcell"
|
||
>
|
||
<div className="day-number">{day.dayOfMonth}</div>
|
||
|
||
{/* Events for this day */}
|
||
<div className="day-events">
|
||
{day.events.map(event => (
|
||
<div
|
||
key={event._id}
|
||
className={`event-pill ${event.category}`}
|
||
title={event.title}
|
||
>
|
||
{event.title}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Event legend */}
|
||
<div className="event-legend" aria-label="Event categories">
|
||
<div className="legend-item social">Social</div>
|
||
<div className="legend-item athletic">Athletic</div>
|
||
<div className="legend-item board">Board</div>
|
||
<div className="legend-item general">General</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default Calendar;
|
||
```
|
||
|
||
## Deployment Architecture
|
||
|
||
### Docker Compose Setup with Security Best Practices
|
||
```yaml
|
||
# docker-compose.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
# Frontend service with multi-stage build (Dockerfile below)
|
||
frontend:
|
||
build:
|
||
context: ./frontend
|
||
dockerfile: Dockerfile
|
||
restart: unless-stopped
|
||
user: node
|
||
ports:
|
||
- "3000:3000"
|
||
depends_on:
|
||
- backend
|
||
env_file: ./frontend/.env
|
||
volumes:
|
||
- ./logs:/app/logs:rw
|
||
networks:
|
||
- app-network
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 512M
|
||
cpus: '0.5'
|
||
|
||
# Backend API service
|
||
backend:
|
||
build:
|
||
context: ./backend
|
||
dockerfile: Dockerfile
|
||
restart: unless-stopped
|
||
user: node
|
||
ports:
|
||
- "4000:4000"
|
||
depends_on:
|
||
- mongodb
|
||
- redis
|
||
env_file: ./backend/.env
|
||
volumes:
|
||
- ./uploads:/app/uploads:rw
|
||
- ./logs:/app/logs:rw
|
||
networks:
|
||
- app-network
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 512M
|
||
cpus: '0.5'
|
||
|
||
# MongoDB service
|
||
mongodb:
|
||
image: mongo:7.0
|
||
restart: unless-stopped
|
||
user: mongodb
|
||
volumes:
|
||
- mongo-data:/data/db:rw
|
||
- ./mongo-init:/docker-entrypoint-initdb.d:ro
|
||
networks:
|
||
- app-network
|
||
env_file: ./backend/.env
|
||
environment:
|
||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
||
command: ["--auth", "--bind_ip_all", "--tlsMode", "preferTLS"]
|
||
healthcheck:
|
||
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017 --quiet
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 1G
|
||
cpus: '1.0'
|
||
|
||
# Redis service
|
||
redis:
|
||
image: redis:alpine
|
||
restart: unless-stopped
|
||
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
|
||
volumes:
|
||
- redis-data:/data:rw
|
||
networks:
|
||
- app-network
|
||
healthcheck:
|
||
test: ["CMD", "redis-cli", "ping"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 256M
|
||
cpus: '0.2'
|
||
|
||
# Nginx service for production
|
||
nginx:
|
||
image: nginx:alpine
|
||
restart: unless-stopped
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
volumes:
|
||
- ./nginx/conf:/etc/nginx/conf.d:ro
|
||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||
- ./frontend/public:/var/www/html:ro
|
||
depends_on:
|
||
- frontend
|
||
- backend
|
||
networks:
|
||
- app-network
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "https://localhost"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 128M
|
||
cpus: '0.1'
|
||
|
||
# Security scanner
|
||
security_scanner:
|
||
image: aquasec/trivy
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
- ./security-reports:/reports
|
||
command: ["image", "--format", "table", "--output", "/reports/scan-$(date +%Y%m%d).txt", "olathedeafclub-frontend:latest", "olathedeafclub-backend:latest"]
|
||
profiles:
|
||
- security
|
||
|
||
# Networks with isolation
|
||
networks:
|
||
app-network:
|
||
driver: bridge
|
||
ipam:
|
||
config:
|
||
- subnet: 172.20.0.0/24
|
||
|
||
# Volumes with backup capability
|
||
volumes:
|
||
mongo-data:
|
||
name: ocd-mongo-data
|
||
redis-data:
|
||
name: ocd-redis-data
|
||
```
|
||
|
||
### Multi-stage Dockerfile for Frontend
|
||
```Dockerfile
|
||
# Build stage
|
||
FROM node:22-alpine AS build
|
||
|
||
WORKDIR /app
|
||
|
||
# Copy package files and install dependencies
|
||
COPY package*.json ./
|
||
RUN npm ci --only=production
|
||
|
||
# Copy source code
|
||
COPY . .
|
||
|
||
# Build the app
|
||
RUN npm run build
|
||
|
||
# Production stage
|
||
FROM node:22-alpine
|
||
|
||
# Create app directory with non-root user
|
||
WORKDIR /app
|
||
RUN addgroup -S appgroup && adduser -S appuser -G appgroup && \
|
||
chown -R appuser:appgroup /app
|
||
|
||
# Copy built assets from build stage
|
||
COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules
|
||
COPY --from=build --chown=appuser:appgroup /app/.next ./.next
|
||
COPY --from=build --chown=appuser:appgroup /app/public ./public
|
||
COPY --from=build --chown=appuser:appgroup /app/package.json ./package.json
|
||
COPY --from=build --chown=appuser:appgroup /app/next.config.js ./next.config.js
|
||
|
||
# Security best practices
|
||
ENV NODE_ENV=production
|
||
RUN npm prune --production
|
||
|
||
# Set permissions and switch to non-root user
|
||
USER appuser
|
||
|
||
# Create health check endpoint
|
||
COPY --from=build --chown=appuser:appgroup /app/health.js ./health.js
|
||
|
||
# Expose port
|
||
EXPOSE 3000
|
||
|
||
# Start the application
|
||
CMD ["npm", "start"]
|
||
```
|
||
|
||
### Nginx Configuration for Production
|
||
```nginx
|
||
# nginx/conf/default.conf
|
||
server {
|
||
listen 80;
|
||
server_name olathedeafclub.com www.olathedeafclub.com;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl;
|
||
server_name olathedeafclub.com www.olathedeafclub.com;
|
||
|
||
ssl_certificate /etc/nginx/ssl/olathedeafclub.com.crt;
|
||
ssl_certificate_key /etc/nginx/ssl/olathedeafclub.com.key;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_prefer_server_ciphers on;
|
||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||
|
||
# Security headers
|
||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
|
||
# Compression
|
||
gzip on;
|
||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||
gzip_min_length 1000;
|
||
|
||
# Frontend static assets
|
||
location / {
|
||
proxy_pass http://frontend:3000;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_set_header Host $host;
|
||
proxy_cache_bypass $http_upgrade;
|
||
}
|
||
|
||
# Backend API
|
||
location /api {
|
||
proxy_pass http://backend:4000;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_set_header Host $host;
|
||
proxy_cache_bypass $http_upgrade;
|
||
}
|
||
|
||
# Static files
|
||
location /static {
|
||
alias /var/www/html;
|
||
expires 30d;
|
||
add_header Cache-Control "public, max-age=2592000";
|
||
}
|
||
|
||
# Media files
|
||
location /uploads {
|
||
alias /app/uploads;
|
||
expires 30d;
|
||
add_header Cache-Control "public, max-age=2592000";
|
||
}
|
||
}
|
||
```
|
||
|
||
## Monitoring & Maintenance
|
||
|
||
### Monitoring Strategy
|
||
- Server health monitoring with Portainer
|
||
- Application performance monitoring
|
||
- Error tracking and notification
|
||
- Database performance monitoring
|
||
- Uptime monitoring for critical services
|
||
|
||
### Backup Strategy
|
||
- Daily MongoDB backups
|
||
- Weekly full system backups
|
||
- Offsite backup storage
|
||
- Automated backup verification
|
||
- Database backup rotation (30-day retention)
|
||
|
||
### Maintenance Schedule
|
||
- Weekly dependency updates review
|
||
- Monthly security patch application
|
||
- Quarterly performance optimization
|
||
- Semi-annual accessibility audit
|
||
- Annual system architecture review
|
||
|
||
## Next Steps After Launch
|
||
|
||
### Phase 1 Extensions
|
||
- Member login portal for personalized experience
|
||
- Enhanced analytics dashboard
|
||
- Content recommendation system based on user interests
|
||
- Advanced search functionality with filters
|
||
- Integration with additional community services
|
||
- Mobile application development
|
||
|
||
### Phase 2 Extensions
|
||
- Live streaming capability for events
|
||
- Member-to-member messaging system
|
||
- Community forum or discussion board
|
||
- Event registration with payment processing
|
||
- Automated membership renewal system
|
||
- Interactive community map
|
||
|
||
This implementation plan provides a comprehensive roadmap for developing the Olathe Club of the Deaf website, addressing all requirements outlined in the project specification while maintaining focus on accessibility, performance, and user experience.
|