A modern web application for viewing and managing Sehri & Iftar schedules during Ramadan. Built with Next.js 16, PostgreSQL, and shadcn/ui. Features Aladhan API integration for fetching prayer times and support for all 64 districts of Bangladesh.
- Today's Schedule: View today's Sehri and Iftar times at a glance
- Full Calendar: Browse complete schedule in a table format
- Location Filter: Filter schedules by city/location
- PDF Download: Download schedules as PDF for offline use
- Dark Mode: Toggle between light and dark themes
- Mobile Responsive: Works seamlessly on all devices
- SSR: Server-side rendered pages for optimal SEO and performance
- Secure Login: Password-protected admin dashboard
- File Upload: Upload schedules via JSON or CSV files
- Drag & Drop: Easy file upload with drag and drop support
- Validation: Real-time validation with error reporting
- Preview: Preview data before confirming upload
- Sample Templates: Download sample JSON/CSV templates
- Dashboard: View statistics and recent uploads
- Framework: Next.js 16 (App Router)
- Language: TypeScript
- Database: PostgreSQL (via Prisma ORM)
- UI Components: shadcn/ui + Tailwind CSS v4
- Authentication: NextAuth.js
- API Integration: Aladhan API for prayer times
- Hadith Integration: hadithapi.pages.dev for random hadiths
- File Parsing: PapaParse (CSV), native JSON parser
- PDF Generation: jsPDF + jspdf-autotable
- Validation: Zod
- Rate Limiting: Token bucket algorithm
- Caching: Multi-layered caching strategy
- Time Handling: moment.js + moment-timezone
- Deployment: Vercel-ready
- Node.js 18+
- PostgreSQL database (local or cloud)
- pnpm (recommended) or npm
-
Clone the repository
git clone <repository-url> cd ramadan-clock
-
Install dependencies
pnpm install
-
Set up environment variables
cp .env.example .env.local
Edit
.env.localwith your configuration. See Environment Variables section for all available options. -
Generate Prisma client
pnpm db:generate
-
Push schema to database
pnpm db:push
-
Seed initial data (optional)
pnpm db:seed
This creates:
- Admin user (email:
admin@example.com, password:admin123) - Sample time entries for testing
- Admin user (email:
-
Run the development server
pnpm dev
Open http://localhost:3000 to see the app.
If you don't see UI changes appearing immediately, use the clean development command:
pnpm dev:cleanFor comprehensive cache clearing:
pnpm clean:allπ See the Cache Troubleshooting Guide for detailed instructions.
ramadan-clock/
βββ app/
β βββ (home)/ # Public pages
β β βββ calendar/ # Full schedule calendar
β β βββ contact/ # Contact page
β βββ admin/ # Admin dashboard
β β βββ dashboard/ # Dashboard overview
β β βββ fetch/ # Fetch from Aladhan API
β β βββ import/ # Import from files
β β βββ upload/ # Upload schedules
β βββ api/ # API routes
β β βββ auth/ # NextAuth authentication
β β βββ cache/ # Cache debugging
β β βββ hadith/ # Hadith API
β β βββ health/ # Health check
β β βββ pdf/ # PDF generation
β β βββ prayer-times/ # Prayer times API
β β βββ progress/ # Progress tracking
β β βββ schedule/ # Schedule CRUD
β βββ auth/ # Login page
β βββ layout.tsx # Root layout
βββ actions/ # Server actions
βββ components/
β βββ admin/ # Admin-specific components
β βββ public/ # Public page components
β βββ seo/ # SEO components
β βββ shared/ # Shared components
β βββ ui/ # shadcn/ui components
βββ features/ # Feature-based architecture (DDD)
β βββ schedule/ # Schedule domain logic
β βββ domain/ # Domain entities & value objects
β βββ repositories/ # Data access layer
β βββ services/ # Business logic
β βββ use-cases/ # Application use cases
βββ hooks/ # Custom React hooks
βββ lib/
β βββ api/ # API utilities & middleware
β βββ cache/ # Caching system
β βββ config/ # Configuration modules
β βββ errors/ # Error handling
β βββ guards/ # Authorization guards
β βββ logger/ # Logging system
β βββ parsers/ # File parsers
β βββ progress/ # Progress tracking
β βββ utils.ts # Utility functions
βββ prisma/
β βββ schema.prisma # Database schema
β βββ seed.ts # Seed script
βββ docs/ # Documentation
βββ api/ # API documentation
βββ *.md # Feature guides
id: UUIDdate: String (YYYY-MM-DD)sehri: String (HH:mm)iftar: String (HH:mm)location: String (nullable)createdAt: DateTime- Unique index on
(date, location)
id: UUIDemail: String (unique)password: String (hashed)createdAt: DateTime
id: UUIDfileName: StringrowCount: Intstatus: String (success/partial/failed)errors: String (JSON)uploadedAt: DateTime
id: UUIDstartDate: String (YYYY-MM-DD format)endDate: String (YYYY-MM-DD format)createdAt: DateTimeupdatedAt: DateTime
[
{
"date": "2026-03-01",
"sehri": "04:45",
"iftar": "18:12",
"location": "Dhaka"
}
]date,sehri,iftar,location
2026-03-01,04:45,18:12,Dhaka- Required fields: date, sehri, iftar
- Optional: location
- Date format: YYYY-MM-DD
- Time format: HH:mm
- Max rows: 1000 per upload
- File size: Max 1MB
- No duplicates: (date + location) must be unique
Default admin credentials (change in production!):
- Email:
admin@example.com - Password:
admin123
Access admin dashboard at: /admin/dashboard
/- Today's Sehri & Iftar/calendar- Full schedule calendar/location/[city]- Location-specific schedule
/auth/login- Admin login/admin/dashboard- Dashboard overview/admin/import- Import schedules from files/admin/fetch- Fetch schedules from Aladhan API/admin/upload- Upload schedules with preview
/api/auth/[...nextauth]- Authentication/api/hadith- Get random hadith/api/health- Health check endpoint/api/pdf- PDF generation/api/prayer-times/fetch- Fetch prayer times from Aladhan API/api/prayer-times/preview- Preview prayer times before import/api/schedule- Schedule CRUD operations/api/schedule/[id]- Single schedule operations/api/schedule/batch- Batch operations/api/progress/[id]- Progress tracking for long operations/api/progress/create- Create new progress operation/api/cache/debug- Cache debugging and statistics
- Real-time today's schedule display
- Location selector dropdown
- Quick links to location pages
- PDF download button
- Responsive card layout
- Full schedule table
- Today highlight badge
- Past/Upcoming status indicators
- Location filter
- Sortable columns
- City-specific schedules
- Dynamic metadata for SEO
- Static generation for performance
- Back navigation
- Total entries count
- Number of locations
- Recent uploads table
- Status badges (Success/Partial/Failed)
- Quick upload button
- Ramadan settings management
- Drag & drop interface
- JSON and CSV support
- Real-time validation
- Error reporting by row
- Preview table (first 10 entries)
- Confirm dialog before upload
- Sample file downloads
- Clean A4 layout
- Header with app title
- Date/location info
- Formatted table with all entries
- Page numbers
- Generation timestamp footer
- Custom filename
- Location-specific exports
- Fetch prayer times for all 64 Bangladesh districts
- Multiple fetch modes:
- Date Range: Fetch for specific date range
- Multi-Month: Fetch for multiple months
- Hijri Month: Fetch for specific Hijri month
- Configurable rate limiting with presets:
- Conservative: 6 req/min
- Balanced: 12 req/min
- Aggressive: 20 req/min
- Fast: 300 req/min
- Turbo: 600 req/min
- Progress tracking for large operations
- Automatic retry with exponential backoff
- Token bucket rate limiting
- Parallel district processing with controlled concurrency
- Random hadith from multiple collections:
- Sahih Bukhari
- Sahih Muslim
- Abu Dawud
- Tirmidhi
- Nasai
- Ibn Majah
- Cached responses (1 hour TTL)
- Rate limited (10 req/min)
- English translations with source references
The application uses intelligent logic to display sehri and iftar times based on the current time and user's location. Here's how it works across different pages:
Returns both today's and tomorrow's schedules along with time status flags:
{
today: TimeEntry | null, // Today's schedule entry
tomorrow: TimeEntry | null, // Tomorrow's schedule entry
sehriPassed: boolean, // Whether today's sehri time has passed
iftarPassed: boolean // Whether today's iftar time has passed
}Logic:
- Fetches today's schedule based on current date (YYYY-MM-DD format)
- Fetches tomorrow's schedule (date + 1 day)
- Compares current time with sehri/iftar times to determine status
- Returns formatted times in 12-hour format (e.g., "04:45 AM")
Returns the appropriate schedule for display:
- Returns today's schedule if iftar time hasn't passed yet
- Returns tomorrow's schedule if today's iftar time has passed
- Returns
nullif no schedule is found
Returns all schedule entries, optionally filtered by location:
- Ordered by date ascending
- Returns formatted times in 12-hour format
- Used for calendar tables and location-specific pages
These helper functions determine if a specific time has passed:
// Parse time string (HH:mm format)
const [hours, minutes] = time.split(':').map(Number);
// Create date object with target time
const targetTime = new Date();
targetTime.setHours(hours, minutes, 0, 0);
// Compare with current time
return now >= targetTime;Note: Time comparisons use the server's system time (Asia/Dhaka timezone).
The home page displays the most relevant schedule based on current time:
- Fetches both today's and tomorrow's schedules via
getScheduleDisplayData() - Determines display schedule:
- If
iftarPassedisfalse: Shows today's sehri and iftar - If
iftarPassedistrue: Shows tomorrow's sehri and iftar
- If
- Visual indicators:
- Sehri card shows "Passed β fast has begun" if sehri time has passed
- Sehri card shows "End time β fast begins" if sehri time is upcoming
- Iftar card shows "Start time β fast breaks"
- Countdown timers: Displayed only when within 1 hour of target time
- Passed schedule card: Shows today's times in a separate card if iftar has passed
The calendar page displays the full schedule in a table format:
- Main cards: Uses
getTodayOrNextDaySchedule()to show today's or tomorrow's schedule - Table rows: Each row shows status based on time comparison:
- Passed (red): Past dates or today after iftar time
- Today (blue): Today before iftar time, or tomorrow after sehri time
- Tomorrow (amber): Tomorrow before sehri time
- Upcoming (default): Future dates beyond tomorrow
- Inline status logic (lines 171-238 in
app/(home)/calendar/page.tsx):// Parse times const sehriTime = parseTime(entry.sehri); // { hours, minutes } const iftarTime = parseTime(entry.iftar); // { hours, minutes } // Get current time const now = new Date(); const currentHours = now.getHours(); const currentMinutes = now.getMinutes(); // Check if time has passed const isTimePast = (hours, minutes) => currentHours > hours || (currentHours === hours && currentMinutes >= minutes);
Location-specific pages use the same logic as the calendar page but filtered by city:
- Validation: Checks if the city exists in the database via
getLocations() - Filtered data: All queries include the location parameter
- Static generation: Uses
generateStaticParams()to pre-render all location pages - Status logic: Identical to calendar page (passed/today/tomorrow/upcoming)
The CountdownTimer component provides real-time countdown:
Features:
- Only visible when within 1 hour of target time
- Updates every second
- Automatically handles next day if target time has passed
- Format:
HH:MM:SSwith pulsing clock icon
Logic:
// Calculate time difference
const diff = targetDate.getTime() - now.getTime();
const oneHourMs = 60 * 60 * 1000;
// Show only if within 1 hour
if (diff <= oneHourMs && diff > 0) {
// Display countdown
}All times are stored in 24-hour format (HH:mm) in the database and converted to 12-hour format for display:
// lib/utils.ts
export function formatTime12Hour(time: string): string {
const [hours, minutes] = time.split(":").map(Number);
const period = hours >= 12 ? "PM" : "AM";
const displayHours = hours % 12 || 12;
return `${displayHours}:${minutes.toString().padStart(2, "0")} ${period}`;
}Example: "04:45" β "4:45 AM", "18:12" β "6:12 PM"
Location filtering works consistently across all pages:
- Location parameter: Passed via URL query param (
?location=Dhaka) or route param (/location/Dhaka) - Server action filtering: All data fetching functions accept optional
locationparameter - "All Locations": When no location is specified, shows entries from all locations
- Location list: Dynamically fetched from database via
getLocations()
User Request
β
Server Component (page.tsx)
β
Server Action (time-entries.ts)
β
Prisma Query (PostgreSQL)
β
Time Formatting & Status Calculation
β
Client Component (countdown timer, UI)
β
Display to User
The application supports all 64 districts of Bangladesh across 8 divisions:
- Barisal: Barguna, Barisal, Bhola, Jhalokati, Patuakhali, Pirojpur
- Chittagong: Bandarban, Brahmanbaria, Chandpur, Chittagong, Comilla, Cox's Bazar, Feni, Khagrachari, Lakshmipur, Noakhali, Rangamati
- Dhaka: Dhaka, Faridpur, Gazipur, Gopalganj, Kishoreganj, Madaripur, Manikganj, Munshiganj, Narayanganj, Narsingdi, Rajbari, Shariatpur, Tangail
- Khulna: Bagerhat, Chuadanga, Jessore, Jhenaidah, Khulna, Kushtia, Magura, Meherpur, Narail, Satkhira
- Mymensingh: Jamalpur, Mymensingh, Netrokona, Sherpur
- Rajshahi: Bogra, Chapainawabganj, Joypurhat, Naogaon, Natore, Pabna, Rajshahi, Sirajganj
- Rangpur: Dinajpur, Gaibandha, Kurigram, Lalmonirhat, Nilphamari, Panchagarh, Rangpur, Thakurgaon
- Sylhet: Habiganj, Moulvibazar, Sunamganj, Sylhet
- Geographic coordinates for accurate prayer times
- Division-based organization
- Location-specific schedules
- Dynamic location pages with SEO metadata
- Location filtering across all pages
This application implements several sophisticated architectural patterns and complex logic systems to ensure scalability, maintainability, and performance. Below are detailed explanations of these advanced features.
The application implements a robust API architecture with a composable middleware pipeline in lib/api/middleware.ts.
The middleware system follows a functional composition pattern where each middleware wraps the handler:
// Compose multiple middleware functions
export function compose(...middlewares: Array<(handler: NextHandler) => NextHandler>) {
return (handler: NextHandler): NextHandler => {
return middlewares.reduceRight(
(acc, middleware) => middleware(acc),
handler
);
};
}-
Request ID Middleware (
withRequestId)- Generates unique request IDs for tracing
- Adds
x-request-idheader to both request and response - Supports external request ID propagation
-
Rate Limiting Middleware (
withRateLimit)- In-memory rate limiting using sliding window algorithm
- Configurable limits and time windows
- Returns rate limit headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - Automatic retry-after calculation for 429 responses
-
Authentication Middleware (
withAuth)- Integrates with NextAuth.js session management
- Supports optional admin role checking
- Injects session data into request headers for downstream use
-
Validation Middleware (
withValidation)- Zod schema validation for query/body parameters
- Detailed error reporting with field-level validation errors
- Supports validation of both query and body data
-
Error Handling Middleware (
withErrorHandler)- Catches and transforms all errors to standardized API responses
- Adds response time tracking via
X-Response-Timeheader - Distinguishes between operational and programming errors
-
Logging Middleware (
withLogging)- Logs all incoming requests with method, URL, and user agent
- Logs successful responses with status and duration
- Logs errors with full context
All API responses follow a consistent structure defined in lib/api/api-response.ts:
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
details?: any;
};
meta?: {
requestId: string;
timestamp: string;
};
}Response helpers include:
success()- Successful responsespaginated()- Paginated data responseserror()- Error responsesvalidationError()- Validation errorsrateLimitExceeded()- Rate limit errors
The ExternalApiClient provides robust external API integration:
Features:
- Timeout Handling: Configurable request timeouts with AbortController
- Retry Logic: Exponential backoff retry for transient failures
- Caching: Built-in in-memory caching with TTL support
- Configurable Retry Options: Custom retryable status codes, delays, and multipliers
Retry Strategy:
const defaultRetryOptions = {
maxRetries: 3,
initialDelay: 1000, // 1 second
maxDelay: 10000, // 10 seconds
backoffMultiplier: 2, // Exponential
retryableStatuses: [408, 429, 500, 502, 503, 504]
};Comprehensive security headers are implemented in lib/api/security-headers.ts:
- X-Content-Type-Options: Prevents MIME type sniffing
- X-Frame-Options: Prevents clickjacking (DENY)
- X-XSS-Protection: Enables XSS filtering
- Referrer-Policy: Controls referrer information
- Permissions-Policy: Restricts browser features
- Content-Security-Policy: Comprehensive CSP policy
- CORS Support: Configurable CORS headers with preflight handling
The application implements a multi-layered caching strategy for optimal performance.
Centralized cache configuration in lib/cache/cache-config.ts:
const CACHE_DURATIONS = {
SHORT: 60, // 1 minute - frequently changing data
MEDIUM: 300, // 5 minutes - moderately changing data
LONG: 900, // 15 minutes - rarely changing data
VERY_LONG: 1800,// 30 minutes - very rarely changing data
HOUR: 3600, // 1 hour - static data
};
const CACHE_TAGS = {
SCHEDULE: 'schedule',
LOCATIONS: 'locations',
STATS: 'stats',
HADITH: 'hadith',
PDF: 'pdf',
};Utility functions in lib/cache/cache-helpers.ts:
createCachedFn()- Wraps async functions with Next.js unstable_cacheinvalidateScheduleCache()- Invalidates all schedule-related cachesinvalidateLocationCache()- Invalidates location cachesinvalidatePdfCache()- Invalidates PDF cachesgetCacheKey()- Creates hierarchical cache keysgetLocationCacheKey()- Creates location-specific cache keysgetDateCacheKey()- Creates date-specific cache keys
The CacheMonitor class provides comprehensive cache performance tracking:
Features:
- Hit/miss tracking per cache key
- Hit rate calculation
- Overall cache statistics
- Per-key statistics
- Metrics export as JSON
- Decorator support for class methods
Usage Example:
// Record hits/misses manually
CacheMonitor.recordHit('schedule:dhaka');
CacheMonitor.recordMiss('schedule:dhaka');
// Get statistics
const stats = CacheMonitor.getOverallStats();
// { hits: 100, misses: 20, hitRate: '83.33%', total: 120 }
// Wrap a function with monitoring
const monitoredFn = withCacheMonitoring(
async () => await fetchData(),
'data-fetch'
);The CacheCleanup class manages periodic cache maintenance:
Features:
- Automatic cleanup of expired cache entries
- Periodic cleanup scheduling (default: 1 hour)
- Manual cleanup triggering
- Cleanup status monitoring
- External API cache cleanup
Initialization:
import { initializeCacheCleanup } from '@/lib/cache';
// Auto-initializes on server start
initializeCacheCleanup();
// Or manually schedule
CacheCleanup.schedulePeriodicCleanup(3600000); // 1 hourThe application implements a comprehensive SEO strategy for maximum search engine visibility.
The lib/seo/metadata.ts module provides metadata generators:
getBaseMetadata()- Base metadata for all pagesgetPageMetadata()- Page-specific metadatagetHomeMetadata()- Home page metadatagetCalendarMetadata()- Calendar page metadatagetLocationMetadata()- Dynamic location page metadatagetContactMetadata()- Contact page metadatagetAdminMetadata()- Admin pages (noindex)getAuthMetadata()- Auth pages (noindex)
Features:
- Open Graph tags for social media sharing
- Twitter Card support
- Canonical URL generation
- Dynamic keyword generation
- Robots meta configuration
- Favicon and manifest configuration
The lib/seo/schemas.ts module generates JSON-LD schemas:
Available Schemas:
createWebSiteSchema()- Website schema with search actioncreateOrganizationSchema()- Organization informationcreateBreadcrumbSchema()- Breadcrumb navigationcreateFAQSchema()- FAQ page schemacreateArticleSchema()- Article/blog post schemacreateSoftwareApplicationSchema()- App store schemacreateLocalBusinessSchema()- Location-specific business schemacreateCollectionPageSchema()- Listing page schemacreateWebPageSchema()- Generic web page schemacreateEventSchema()- Event schema for RamadancreateHowToSchema()- How-to guide schema
The application follows Domain-Driven Design (DDD) principles with a feature-based architecture in features/schedule/.
features/schedule/
βββ domain/
β βββ entities/ # Domain entities with business logic
β βββ value-objects/ # Value objects with validation
β βββ types/ # Domain types and interfaces
βββ repositories/ # Data access layer
βββ services/ # Business logic services
βββ use-cases/ # Application use cases
The TimeEntry entity encapsulates schedule data with domain logic:
Features:
- Immutable properties (readonly)
- Time comparison methods:
isSehriPassed(),isIftarPassed() - Date comparison methods:
isPast(),isToday(),isTomorrow() - DTO conversion:
toDTO(),toFormattedDTO() - Static factory methods:
fromDTO(),fromDTOArray()
The LocationVO value object encapsulates location logic:
Features:
- Null-safe location handling
- Display name generation
- Validation and normalization
- Equality comparison
- Static factory methods:
create(),all()
The ScheduleService contains business logic:
Methods:
getTodaySchedule()- Get today's entrygetTomorrowSchedule()- Get tomorrow's entrygetTodayOrNextDaySchedule()- Smart schedule selectiongetScheduleDisplayData()- Complete display datagetFullSchedule()- All entriesgetScheduleByDateRange()- Date range querygetLocations()- Unique locationsgetStats()- Dashboard statisticsgetTimeEntryById()- Single entry lookupupdateTimeEntry()- Update entrydeleteTimeEntry()- Delete entry
Use cases encapsulate application-level logic:
GetTodayScheduleUseCaseGetFullScheduleUseCaseGetScheduleDisplayDataUseCaseGetLocationsUseCaseUploadScheduleUseCaseUpdateEntryUseCaseDeleteEntryUseCase
The application implements a comprehensive error handling system in lib/errors/app-error.ts.
All errors extend the base AppError class:
DatabaseError- Database operation failuresValidationError- Input validation failuresNotFoundError- Resource not foundUnauthorizedError- Authentication failuresForbiddenError- Authorization failuresConflictError- Resource conflictsFileUploadError- File upload failures
Features:
- HTTP status code mapping
- Operational vs programming error distinction
- Error cause chaining
- JSON serialization
- Type guard:
isAppError() - Error converter:
toAppError()
The application uses a factory pattern for file parsing in lib/parsers/index.ts.
The ParserFactory manages parser registration and selection:
Features:
- Automatic parser selection based on file extension
- Extensible parser registration
- Support for multiple file formats (JSON, CSV)
- Type-safe parsing with
ParsedScheduleEntryinterface
Available Parsers:
JsonParser- JSON file parsingCsvParser- CSV file parsing with PapaParse
Usage:
const parser = ParserFactory.getParser('schedule.json');
const entries = await parser.parse(fileContent);A comprehensive logging system is implemented in lib/logger/logger.ts.
- Multiple Log Levels: error, warn, info, debug
- Environment-Aware: Different behavior for dev/test/production
- Console Logging: Color-coded output for development
- Context Support: Structured logging with context objects
- Child Loggers: Create loggers with default context
- Production-Ready: Placeholder for external service integration (Sentry, Datadog)
Usage:
import { logger, createLogger } from '@/lib/logger';
// Global logger
logger.error('Error message', { context: 'value' }, error);
// Child logger with context
const apiLogger = createLogger('api');
apiLogger.info('API request', { endpoint: '/schedule' });Centralized configuration management in lib/config/index.ts.
APP_CONFIG- Application metadataUPLOAD_CONFIG- File upload limits and allowed typesTIME_CONFIG- Time-related constantsPDF_CONFIG- PDF generation settingsUI_CONFIG- UI display limitslocations.config.ts- Location dataenv.config.ts- Environment variables
The application implements a dual-layer guard system in lib/guards/.
The DashboardGuard component protects client-side routes:
Features:
- Session checking
- Redirect to login if unauthenticated
- Loading state during authentication check
- Configurable options (requireAdmin)
The dashboardGuard function protects server components:
Features:
- Server-side session validation
- Early return for unauthenticated requests
- Type-safe options
The schedule display system implements sophisticated time-based logic.
Using moment.js for accurate time comparisons:
// Check if sehri/iftar time has passed
isSehriPassed(referenceDate?: Date): boolean {
const now = referenceDate ? moment(referenceDate) : moment();
const sehriTime = moment(this.sehri, 'HH:mm');
sehriTime.set({
year: now.year(),
month: now.month(),
date: now.date(),
});
return now.isSameOrAfter(sehriTime);
}The getTodayOrNextDaySchedule() method intelligently selects the appropriate schedule:
Logic:
- Fetch today's schedule
- Check if today's iftar has passed
- If iftar passed, return tomorrow's schedule
- Otherwise, return today's schedule
The ScheduleDisplayData interface provides complete display information:
interface ScheduleDisplayData {
today: TimeEntry | null;
tomorrow: TimeEntry | null;
sehriPassed: boolean;
iftarPassed: boolean;
}The CountdownTimer component provides real-time countdown:
Features:
- Only visible within 1 hour of target time
- Updates every second
- Automatically handles next day transitions
- Format:
HH:MM:SSwith pulsing clock icon
The RateLimiter class provides in-memory rate limiting:
Features:
- Sliding window algorithm
- Per-identifier tracking (IP or user ID)
- Configurable limits and time windows
- Automatic cleanup of expired entries
- Store size monitoring
- Singleton pattern for global instance
Usage:
const rateLimiter = new RateLimiter();
const result = await rateLimiter.checkLimit('user:123', 100, 60000);
// { allowed: true, remaining: 99, resetAt: Date }Comprehensive validation using Zod schemas in lib/validations/api-schemas.ts.
Features:
- Request validation schemas
- Response validation schemas
- Type-safe validation
- Detailed error reporting
- Input Sanitization:
sanitizeInput()prevents XSS attacks - URL Validation:
sanitizeUrl()prevents open redirects - IP Detection:
getClientIp()handles various proxy headers - User Agent:
getUserAgent()extracts user agent information
- Next.js Caching: Uses
unstable_cachefor data caching - Static Generation: Location pages pre-rendered with
generateStaticParams() - Cache Tags: Selective cache invalidation
- Stale-While-Revalidate: Background revalidation for improved UX
- External API Caching: Built-in caching with TTL
- Request Tracking: Unique request IDs for tracing
- Response Time:
X-Response-Timeheader - Cache Metrics: Hit/miss tracking and hit rate calculation
- Structured Logging: Context-aware logging throughout the application
- Error Tracking: Comprehensive error logging with context
Built with shadcn/ui:
- Card, Button, Table, Dialog
- Alert, Toast (Sonner), Tabs
- Select, Badge, Skeleton
- Dropdown Menu, Input, Label
- Admin routes protected by NextAuth middleware
- Password hashing with bcryptjs
- Server-side validation
- File type restrictions
- File size limits
- Rate limiting with token bucket algorithm
- Request ID tracking
- Security headers (CSP, CORS, XSS protection)
- Input sanitization
- IP-based rate limiting
- Push code to GitHub
- Import project in Vercel
- Set environment variables (see Environment Variables section)
- Deploy
DATABASE_URL="postgresql://<user>:<password>@<host>:<port>/<database>"
NEXTAUTH_SECRET="<generate-with-openssl-rand-base64-32>"
NEXTAUTH_URL="https://your-domain.com"
ADMIN_EMAIL="admin@example.com"
ADMIN_PASSWORD="secure-password"
TIMEZONE="Asia/Dhaka"
RAMADAN_START_DATE="2026-02-19"
RAMADAN_END_DATE="2026-03-20"
ALLOWED_ORIGINS="https://your-domain.com"- Supabase: https://supabase.com (Free tier available)
- Neon: https://neon.tech (Free tier available)
- Railway: https://railway.app (PostgreSQL included)
- Prisma Cloud: https://www.prisma.io/data-platform
pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Start production server
pnpm lint # Run ESLint
pnpm db:generate # Generate Prisma client
pnpm db:push # Push schema to database
pnpm db:seed # Seed initial data| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://user:pass@localhost:5432/ramadan-clock |
NEXTAUTH_SECRET |
Secret for session encryption | Generate with openssl rand -base64 32 |
NEXTAUTH_URL |
Application URL | http://localhost:3000 |
ADMIN_EMAIL |
Admin login email | admin@example.com |
ADMIN_PASSWORD |
Admin login password | secure-password |
| Variable | Description | Default |
|---|---|---|
NODE_ENV |
Environment mode | development |
TIMEZONE |
Application timezone | Asia/Dhaka |
ALLOWED_ORIGINS |
CORS allowed origins (comma-separated) | http://localhost:3000 |
RAMADAN_START_DATE |
Ramadan start date (YYYY-MM-DD) | - |
RAMADAN_END_DATE |
Ramadan end date (YYYY-MM-DD) | - |
PROJECT_REPO_URL |
Project repository URL | - |
DEVELOPER_NAME |
Developer name | - |
DEVELOPER_BIO |
Developer bio | - |
DEVELOPER_GITHUB |
GitHub profile URL | - |
DEVELOPER_LINKEDIN |
LinkedIn profile URL | - |
DEVELOPER_EMAIL |
Contact email | - |
Note: Aladhan API configuration is hardcoded in lib/config/app.config.ts and includes:
- Base URL:
https://api.aladhan.com/v1 - Method: ISNA (Islamic Society of North America)
- Country: Bangladesh
- Timezone: Asia/Dhaka
- Rate limiting: Token bucket with 50 capacity, 5 tokens/second
To modify these settings, edit the ALADHAN_CONFIG object in the configuration file directly.
- Copy
.env.examplefor all available variables - See lib/config/env.config.ts for validation rules
- Aladhan API Implementation - Prayer times integration
- API Keys Setup Guide - External API configuration
- Cache Troubleshooting Guide - Caching issues
- Caching Implementation Guide - Cache system overview
- Rate Limiting Guide - Rate limiting configuration
- SEO Implementation Summary - SEO features
- OpenAPI Specification - Full API documentation
- API Usage Guide - API usage examples
Contributions are welcome! Please feel free to submit a Pull Request.
This project is open source and available under the MIT License.
- Built for the Muslim community during Ramadan
- Inspired by the need for accurate prayer time information
- Made with β€οΈ using modern web technologies
Ramadan Mubarak! π