MultiView Calendar Health Care Appointment Management System β Next.js, Postgresql FullStack Project (Admin Control Panel Permission Dashboard)
A modern, full-featured calendar and appointment management web application built with Next.js, React, and PostgreSQL. Perfect for healthcare, clinics, and organizations needing robust scheduling, filtering, and client management with multiple calendar views, instant search, advanced filtering, and a clean, responsive UI.
Live Demo: https://doctor-patient-calendar-appointment.vercel.app/
- Project Overview
- Features
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- Installation & Setup
- Running the Project
- Project Walkthrough
- API Endpoints
- Components & Reusability
- Routes & Navigation
- Key Functionalities
- Code Examples
- Database Schema
- Authentication & Authorization
- Keywords
- Conclusion
This is a comprehensive appointment management system designed for healthcare providers, clinics, and service businesses. It provides a complete solution for scheduling, managing appointments, handling invitations, and controlling access permissions. The application features three different calendar views (List, Week, Month), real-time filtering, search functionality, and a robust permission system.
- Multi-View Calendar: Switch between List, Week, and Month views
- Real-time Search: Instant search across all appointment fields
- Advanced Filtering: Filter by category, patient, date, and status
- Invitation System: Send and manage appointment/dashboard invitations
- Permission Management: Role-based access control (owner, full, write, read)
- Responsive Design: Works seamlessly on desktop, tablet, and mobile
- Type-Safe: Built with TypeScript for better developer experience
- Modern UI: Clean interface using Tailwind CSS and shadcn/ui components
- List View: Appointments grouped by date in a scrollable list format
- Week View: Weekly calendar grid showing appointments in time slots
- Month View: Monthly calendar grid with appointment indicators
- Create, read, update, and delete appointments
- Add attachments, notes, activities, and assignees
- Set categories, locations, and status (done, pending, alert)
- Link appointments to patients and relatives
- Real-time search across title, notes, location, and patient information
- Filter by category, patient, date range, and status
- Combine multiple filters for precise results
- Send invitations for appointment access
- Send invitations for dashboard access
- Email notifications with secure token-based links
- Track invitation status (pending, accepted, declined)
- Owner: Full control (creator of appointment)
- Full: Read, write, and delete access
- Write: Read and modify access
- Read: View-only access
- Secure authentication with custom JWT-based system
- Email verification required
- User profile management
- Session-based authentication with secure cookies
- Next.js 15.4.10: React framework with App Router
- React 18.3.1: UI library
- TypeScript 5: Type-safe development
- Tailwind CSS 4: Utility-first CSS framework
- shadcn/ui: High-quality component library
- Radix UI: Accessible component primitives
- date-fns: Date manipulation and formatting
- Lucide React: Icon library
- PostgreSQL: Self-hosted PostgreSQL database
- Direct database connection via
pglibrary - Custom JWT-based authentication system
- Server-side permission validation
- Vercel Blob storage for attachments
- Direct database connection via
- Nodemailer: Email sending functionality
- UUID: Secure token generation
- Redoc: API documentation
- ESLint: Code linting
- Turbopack: Fast bundler (Next.js)
- TypeScript: Static type checking
multiview-calender-appointment/
βββ public/ # Static assets
β βββ favicon.ico # Site icon
β βββ redoc.html # API documentation
βββ src/
β βββ app/ # Next.js App Router
β β βββ api/ # API routes
β β β βββ appointments/ # Appointment endpoints
β β β β βββ [id]/ # Dynamic route for specific appointment
β β β β β βββ route.ts # GET, PUT, PATCH, DELETE
β β β β β βββ permissions/ # Permission endpoints
β β β β βββ route.ts # GET (list), POST (create)
β β β β βββ search/ # Search endpoint
β β β βββ invitations/ # Invitation endpoints
β β β β βββ accept/ # Accept invitation
β β β β βββ route.ts # GET, POST invitations
β β β βββ users/ # User search
β β β βββ dashboard/ # Dashboard permissions
β β β βββ openapi/ # OpenAPI specification
β β βββ accept-invitation/ # Invitation acceptance page
β β βββ api-docs/ # API documentation page
β β βββ api-status/ # API health check page
β β βββ control-panel/ # Permission management
β β βββ login/ # Login page
β β βββ logout/ # Logout page
β β βββ register/ # Registration page
β β βββ layout.tsx # Root layout with metadata
β β βββ page.tsx # Home page (calendar)
β β βββ AuthLayout.tsx # Authentication-based layout
β βββ components/ # React components
β β βββ calendar/ # Calendar-related components
β β β βββ AppointmentDialog.tsx # Create/edit appointment
β β β βββ AppointmentList.tsx # List view
β β β βββ CalendarHeader.tsx # View switcher & navigation
β β β βββ MonthView.tsx # Month calendar view
β β β βββ WeekView.tsx # Week calendar view
β β β βββ Filters.tsx # Filter controls
β β β βββ SearchBar.tsx # Search input
β β β βββ AppointmentHoverCard.tsx # Appointment preview
β β βββ control-panel/ # Permission management
β β β βββ AppointmentAccessPermission.tsx
β β β βββ UserAccessPermission.tsx
β β β βββ InvitationList.tsx
β β βββ ui/ # Reusable UI primitives
β β β βββ button.tsx
β β β βββ dialog.tsx
β β β βββ input.tsx
β β β βββ select.tsx
β β β βββ ...
β β βββ AuthGuard.tsx # Route protection
β β βββ login/ # Login component
β β βββ register/ # Registration component
β β βββ navbar/ # Navigation bar
β βββ context/ # React Context providers
β β βββ DateContext.tsx # Global date state
β β βββ AppointmentColorContext.tsx # Color management
β βββ lib/ # Utility libraries
β β βββ postgresClient.ts # PostgreSQL database client
β β βββ auth.ts # Custom JWT authentication
β β βββ vercelBlob.ts # Vercel Blob storage client
β β βββ session.ts # Session management
β β βββ email.ts # Email sending
β β βββ permissions.ts # Permission checking
β β βββ utils.ts # General utilities
β βββ types/ # TypeScript type definitions
β β βββ types.ts # Main types
β β βββ invitation.ts # Invitation types
β βββ styles/
β βββ globals.css # Global styles
βββ migrations/ # Database migration SQL files
β βββ 001_initial_schema.sql # Initial database schema
β βββ 002_add_password_to_users.sql # Password authentication support
β βββ 003_add_performance_indexes.sql # Performance optimizations
βββ scripts/ # Database utility scripts
β βββ migrate.ts # Run database migrations
β βββ seed.ts # Seed database from CSV files
β βββ check-users.ts # List all users in database
β βββ manage-user.ts # Manage user verification/passwords
βββ package.json # Dependencies
βββ tsconfig.json # TypeScript config
βββ next.config.ts # Next.js config
βββ tailwind.config.ts # Tailwind config
βββ README.md # This fileBefore you begin, ensure you have the following installed:
- Node.js 18.x or higher
- npm or yarn or pnpm package manager
- Git for version control
- A PostgreSQL database (self-hosted or cloud provider)
- A Vercel account (for Vercel Blob storage, free tier works)
Create a .env.local file in the root directory with the following variables:
# PostgreSQL Database Configuration
# Format: postgresql://username:password@host:port/database
DATABASE_URL=postgresql://username:password@host:port/database
# Authentication Secret (JWT)
# Generate with: openssl rand -base64 32
AUTH_SECRET=your-jwt-secret-key-here
# Application Base URL
# For local development: http://localhost:3000
# For production: https://your-domain.com
NEXT_PUBLIC_BASE_URL=http://localhost:3000
# Vercel Blob Storage
# Get from Vercel Dashboard β Storage β Blob
BLOB_READ_WRITE_TOKEN=vercel_blob_rw_xxxxxxxxxxxxx
# Email Configuration (Gmail SMTP)
# For Gmail, you'll need to:
# 1. Enable 2-Factor Authentication
# 2. Generate an "App Password" in Google Account settings
EMAIL_USER=your-email@gmail.com
EMAIL_PASS=your-app-password- Set Up PostgreSQL Database:
- Use a self-hosted PostgreSQL instance, or
- Use a cloud provider (AWS RDS, DigitalOcean, Hetzner, etc.)
- Create Database and User:
- Create a database for the project
- Create a user with appropriate permissions
- Note the connection details (host, port, database name, username, password)
- Format Connection String:
- Use format:
postgresql://username:password@host:port/database - Example:
postgresql://myuser:mypass@localhost:5432/myapp_db
- Use format:
- Create Vercel Account: Go to vercel.com and sign up
- Create a Blob Store:
- Go to your Vercel project dashboard
- Navigate to Storage β Blob
- Click "Create Database" (if first time) or "Create Blob Store"
- Give it a name (e.g., "appointments-attachments")
- Get Read/Write Token:
- In the Blob Store settings, find "Tokens"
- Create a new token with read/write permissions
- Copy the token β
BLOB_READ_WRITE_TOKEN
-
Using OpenSSL (recommended):
openssl rand -base64 32
-
Using Node.js:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" -
Copy the output β
AUTH_SECRETin your.env.localfile
- Go to your Google Account settings
- Enable 2-Step Verification
- Go to "App passwords" section
- Generate a new app password for "Mail"
- Use this password (not your regular Gmail password) for
EMAIL_PASS
- Never commit
.env.localto version control (it's in.gitignore) - The
AUTH_SECRETis used to sign JWT tokens - keep it secret and never expose it! - The
DATABASE_URLcontains database credentials - protect it like a password - The
BLOB_READ_WRITE_TOKENgrants access to your file storage - keep it secure - Use different credentials for development and production
- Rotate credentials if they're ever exposed
- Generate strong, random values for
AUTH_SECRET(minimum 32 characters)
git clone <repository-url>
cd multiview-calender-appointmentnpm install
# or
yarn install
# or
pnpm installCreate .env.local file and add all required environment variables (see Environment Variables section above).
You'll need to create the database schema using the migration files:
The project includes migration SQL files in the migrations/ directory:
# Run migrations to create all tables
npm run db:migrateThis will create the following tables:
- users - User profiles with authentication
- appointments - Appointment records
- appointment_assignee - Appointment invitations and permissions
- patients - Patient information
- relatives - Relative information
- categories - Appointment categories
- activities - Appointment activity log
- dashboard_access - Dashboard access permissions
CREATE TABLE appointments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE,
start TIMESTAMP WITH TIME ZONE NOT NULL,
end TIMESTAMP WITH TIME ZONE NOT NULL,
location TEXT,
patient UUID REFERENCES patients(id) ON DELETE SET NULL,
attachements TEXT[],
category UUID REFERENCES categories(id) ON DELETE SET NULL,
notes TEXT,
title TEXT NOT NULL,
status TEXT CHECK (status IN ('done', 'pending', 'alert')),
user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL
);Note: The migration files are idempotent (safe to run multiple times). They use
CREATE TABLE IF NOT EXISTSandCREATE INDEX IF NOT EXISTSto avoid errors.
If you have CSV data to import:
# Set CSV_DIR environment variable to your CSV files location
export CSV_DIR=/path/to/your/csv/files
# Run seed script
npm run db:seedVercel Blob storage is automatically configured when you set the BLOB_READ_WRITE_TOKEN environment variable. The application will use Vercel Blob for storing appointment attachments.
- Ensure
BLOB_READ_WRITE_TOKENis set in your.env.local - The application will automatically create blob stores as needed
- Files are uploaded via
/api/storage/uploadendpoint
npm run dev
# or
yarn dev
# or
pnpm devThe application will start at http://localhost:3000
npm run build
npm startnpm run lintThe project includes several database utility scripts:
# Run database migrations (creates all tables)
npm run db:migrate
# Seed database from CSV files (requires CSV_DIR environment variable)
npm run db:seed
# Check all users in the database
npm run db:check-users
# Manage user (verify email, set password)
npm run db:manage-user -- --email user@example.com --verify --set-password newpassword- Registration: New users register with email and password
- Email Verification: Users receive verification email and must verify their email address
- Login: After verification, users can log in with email and password
- Session Creation: JWT token is created and stored in secure HTTP-only cookie
- User Record: User data is stored in the
userstable with hashed password
- Home Page: Users land on the calendar view (default: List view)
- View Switching: Users can switch between List, Week, and Month views
- Navigation: Use arrow buttons to navigate between dates (Week/Month views)
- Creating Appointments: Click "New Appointment" button to open creation dialog
- Viewing Appointments: Click on appointments to view details
- Editing: Click edit icon (if you have write permissions)
- Filtering: Use filter controls to narrow down appointments
- Search: Use search bar for instant search across all fields
- Send Invitation: Go to Control Panel β Select appointment/dashboard
- Enter Email: Provide invitee's email and select permission level
- Email Sent: System sends email with secure invitation link
- Accept Invitation: Invitee clicks link and accepts invitation
- Access Granted: Invitee now has specified permission level
- Owner: Created the appointment - full control
- Invited Users: Receive invitation with permission level
- Permission Check: System checks permissions before allowing actions
- UI Adaptation: Interface adapts based on user's permission level
List all appointments (with optional filtering).
Query Parameters:
user_id(optional): Filter by user ID
Response:
{
"appointments": [
{
"id": "uuid",
"title": "Appointment Title",
"start": "2025-01-15T10:00:00Z",
"end": "2025-01-15T11:00:00Z",
"location": "Room 101",
"status": "pending",
"user_id": "user-uuid"
}
]
}Create a new appointment.
Request Body:
{
"title": "Appointment Title",
"start": "2025-01-15T10:00:00Z",
"end": "2025-01-15T11:00:00Z",
"location": "Room 101",
"user_id": "user-uuid",
"category": "category-uuid",
"patient": "patient-uuid",
"notes": "Additional notes"
}Get a specific appointment by ID.
Full update of an appointment (replace all fields).
Partial update of an appointment (update only provided fields).
Delete an appointment.
Search appointments by query string.
Query Parameters:
query: Search term
Create and send an invitation.
Request Body:
{
"type": "appointment" | "dashboard",
"email": "invitee@example.com",
"resourceId": "appointment-id-or-user-id",
"permission": "read" | "write" | "full",
"invitedUserId": "user-id" // optional
}Get all invitations for the current user.
Accept an invitation by token.
Request Body:
{
"token": "invitation-token",
"userId": "user-id"
}Search users by email or display name.
Query Parameters:
query: Search term
Get permission level for current user on an appointment.
Discard/delete an appointment invitation.
Get OpenAPI specification in JSON format.
Interactive API documentation page.
Full interactive API documentation using Redoc.
Displays appointments in a list format grouped by date.
Usage:
import AppointmentList from '@/components/calendar/AppointmentList';
export default function MyPage() {
return <AppointmentList />;
}Features:
- Automatic data fetching
- Search and filter integration
- Permission-based UI
- Edit/delete functionality
Displays appointments in a monthly calendar grid.
Usage:
import MonthView from '@/components/calendar/MonthView';
export default function CalendarPage() {
return <MonthView />;
}Displays appointments in a weekly calendar grid.
Usage:
import WeekView from '@/components/calendar/WeekView';
export default function WeekCalendar() {
return <WeekView />;
}Dialog component for creating/editing appointments.
Usage:
import AppointmentDialog from '@/components/calendar/AppointmentDialog';
import { Button } from '@/components/ui/button';
export default function MyComponent() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>New Appointment</Button>
<AppointmentDialog
isOpen={open}
onOpenChange={setOpen}
onSuccess={() => {
// Handle success
setOpen(false);
}}
/>
</>
);
}Header component with view switcher and date navigation.
Usage:
import CalendarHeader from '@/components/calendar/CalendarHeader';
const [view, setView] = useState<"Liste" | "Woche" | "Monat">("Liste");
<CalendarHeader view={view} setView={setView} />Provides global date state for calendar navigation.
Usage:
import { DateProvider } from '@/context/DateContext';
function App() {
return (
<DateProvider>
<YourCalendarComponents />
</DateProvider>
);
}Access in components:
import { useDateContext } from '@/context/DateContext';
function MyComponent() {
const { currentDate, setCurrentDate } = useDateContext();
// Use currentDate and setCurrentDate
}Provides deterministic color assignment for appointments.
Usage:
import { AppointmentColorProvider } from '@/context/AppointmentColorContext';
function App() {
return (
<AppointmentColorProvider>
<YourComponents />
</AppointmentColorProvider>
);
}Access in components:
import { useAppointmentColor } from '@/context/AppointmentColorContext';
function MyComponent() {
const { randomBgColor } = useAppointmentColor();
const color = randomBgColor(appointment.category || appointment.id);
}All UI components in src/components/ui/ are reusable and can be used throughout your application.
Example:
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog';
function MyForm() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<Input placeholder="Enter text" />
</DialogContent>
</Dialog>
);
}- Copy Component Files: Copy the component file to your project
- Install Dependencies: Ensure required dependencies are installed
- Update Imports: Adjust import paths to match your project structure
- Copy Dependencies: Copy any related types, utilities, or contexts
- Test Integration: Test the component in your project context
Example - Reusing AppointmentDialog:
# Copy the component
cp src/components/calendar/AppointmentDialog.tsx your-project/src/components/
# Copy required types
cp src/types/types.ts your-project/src/types/
# Copy required utilities
cp src/lib/permissions.ts your-project/src/lib//login- User login page/register- User registration page/accept-invitation- Accept invitation by token
/- Home page with calendar views/control-panel- Permission and invitation management/api-docs- API documentation/api-status- API health check
All API routes are prefixed with /api/:
/api/appointments- Appointment CRUD operations/api/appointments/[id]- Individual appointment operations/api/invitations- Invitation management/api/users/search- User search/api/openapi- OpenAPI specification
Login β Email Verification β Home (Calendar)
β
Control Panel β Send Invitations
β
Accept Invitation β Access Granted
The application supports three different calendar views:
- List View: Chronological list of appointments grouped by date
- Week View: Weekly grid showing appointments in time slots
- Month View: Monthly grid with appointment indicators
Implementation:
- Uses React Context (
DateContext) for shared date state - Conditional rendering based on selected view
- Optimized performance with lazy loading
Search functionality that queries across multiple fields:
- Appointment title
- Notes
- Location
- Patient information
Implementation:
- Debounced search input
- Server-side search endpoint
- Client-side filtering for instant results
Multi-criteria filtering system:
- By Category: Filter appointments by category
- By Patient: Show appointments for specific patients
- By Date: Filter by date range
- By Status: Filter by appointment status (done, pending, alert)
Implementation:
- State management for filter values
- Combined filter application
- URL query parameter support (optional)
Role-based access control with four permission levels:
- Owner: Full control (created the appointment)
- Full: Read, write, and delete access
- Write: Read and modify access
- Read: View-only access
Implementation:
- Permission checking utility (
getUserAppointmentPermission) - UI adaptation based on permissions
- Server-side permission validation
Secure invitation system with email notifications:
- Generate unique tokens (UUID)
- Send email invitations
- Track invitation status
- Accept/decline invitations
Implementation:
- Token-based invitation links
- Email sending with Nodemailer
- Database tracking of invitation status
Secure authentication flow:
- Email/password authentication
- Email verification required
- Session management
- Route protection with
AuthGuard
Implementation:
- Custom JWT-based authentication system
- Server-side authentication with secure password hashing
- Session management with HTTP-only cookies
- API route protection with authentication middleware
import { useState } from 'react';
import AppointmentDialog from '@/components/calendar/AppointmentDialog';
function CreateAppointmentButton() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>
New Appointment
</button>
<AppointmentDialog
isOpen={open}
onOpenChange={setOpen}
onSuccess={() => {
console.log('Appointment created!');
setOpen(false);
}}
/>
</>
);
}import { useEffect, useState } from 'react';
import type { Appointment } from '@/types/types';
function AppointmentList() {
const [appointments, setAppointments] = useState<Appointment[]>([]);
useEffect(() => {
async function fetchAppointments() {
try {
const response = await fetch('/api/appointments', {
credentials: 'include', // Include cookies for authentication
});
if (!response.ok) {
throw new Error('Failed to fetch appointments');
}
const data = await response.json();
setAppointments(data.appointments || []);
} catch (error) {
console.error('Error fetching appointments:', error);
}
}
fetchAppointments();
}, []);
return (
<div>
{appointments.map(appointment => (
<div key={appointment.id}>
<h3>{appointment.title}</h3>
<p>{appointment.start}</p>
</div>
))}
</div>
);
}import { useDateContext } from '@/context/DateContext';
import { format } from 'date-fns';
function DateDisplay() {
const { currentDate, setCurrentDate } = useDateContext();
return (
<div>
<p>Current Date: {format(currentDate, 'MMMM dd, yyyy')}</p>
<button onClick={() => setCurrentDate(new Date())}>
Go to Today
</button>
</div>
);
}import { getUserAppointmentPermission } from '@/lib/permissions';
import type { Appointment, AppointmentAssignee } from '@/types/types';
function AppointmentActions({
appointment,
assignees,
userId
}: {
appointment: Appointment;
assignees?: AppointmentAssignee[];
userId: string;
}) {
const permission = getUserAppointmentPermission({
appointment,
assignees,
userId
});
const canEdit = permission === 'owner' || permission === 'write' || permission === 'full';
const canDelete = permission === 'owner' || permission === 'full';
return (
<div>
{canEdit && <button>Edit</button>}
{canDelete && <button>Delete</button>}
{!canEdit && <p>Read-only access</p>}
</div>
);
}async function sendInvitation() {
const response = await fetch('/api/invitations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'appointment',
email: 'invitee@example.com',
resourceId: 'appointment-id',
permission: 'read',
}),
});
const data = await response.json();
if (response.ok) {
console.log('Invitation sent:', data);
} else {
console.error('Error:', data.error);
}
}Stores user profile information with custom authentication.
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT NOT NULL UNIQUE,
password_hash TEXT,
email_verified BOOLEAN DEFAULT false,
email_verification_token UUID,
password_reset_token UUID,
password_reset_expires TIMESTAMP WITH TIME ZONE,
display_name TEXT,
role TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);Main table for appointment records.
CREATE TABLE appointments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE,
start TIMESTAMP WITH TIME ZONE NOT NULL,
end TIMESTAMP WITH TIME ZONE NOT NULL,
location TEXT,
patient UUID REFERENCES patients(id),
attachements TEXT[],
category UUID REFERENCES categories(id),
notes TEXT,
title TEXT NOT NULL,
status TEXT CHECK (status IN ('done', 'pending', 'alert')),
user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL
);Tracks appointment invitations and permissions.
CREATE TABLE appointment_assignee (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
appointment UUID REFERENCES appointments(id) ON DELETE CASCADE NOT NULL,
"user" UUID REFERENCES users(id) ON DELETE SET NULL,
user_type TEXT CHECK (user_type IN ('users', 'relatives', 'patients')),
invited_email TEXT,
status TEXT CHECK (status IN ('pending', 'accepted', 'declined')),
permission TEXT CHECK (permission IN ('read', 'write', 'full')),
invitation_token UUID UNIQUE,
invited_by UUID REFERENCES users(id) ON DELETE SET NULL
);Patient information.
CREATE TABLE patients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
firstname TEXT NOT NULL,
lastname TEXT NOT NULL,
birth_date DATE,
care_level INTEGER,
pronoun TEXT,
email TEXT,
active BOOLEAN DEFAULT true,
active_since TIMESTAMP WITH TIME ZONE
);Appointment categories.
CREATE TABLE categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE,
label TEXT NOT NULL,
description TEXT,
color TEXT,
icon TEXT
);- Registration: User creates account with email/password
- Password Hashing: Password is hashed using bcrypt before storage
- Email Verification: System sends verification email with unique token
- Verification: User clicks link to verify email
- Login: User logs in with verified credentials
- JWT Token: System generates JWT token with user ID and email
- Session Cookie: JWT token stored in secure HTTP-only cookie
- User Record: User data stored in
userstable with hashed password
- Permission Check: System checks user's permission level
- UI Adaptation: Interface shows/hides actions based on permissions
- API Validation: Server-side validation of permissions
- Access Control: Unauthorized actions are blocked
- Password Hashing: Passwords hashed with bcrypt (salt rounds: 10)
- JWT Tokens: Secure token-based authentication with expiration
- HTTP-Only Cookies: Session tokens stored in secure, HTTP-only cookies
- Server-Side Validation: All authentication and authorization checks on server
- Token-based Invitations: Secure UUID tokens for invitations
- Email Verification: Required before accessing app
- Rate Limiting: API endpoints protected against brute force attacks
- Input Validation: All user inputs validated and sanitized
Next.js, React, TypeScript, PostgreSQL, Vercel Blob, Tailwind CSS, shadcn/ui, Radix UI, Calendar, Appointment Management, Healthcare, Medical Scheduling, Multi-View Calendar, List View, Week View, Month View, Search, Filtering, Invitation System, Permission Management, Role-Based Access Control, RESTful API, OpenAPI, JWT Authentication, Custom Auth, Authorization, Email Notifications, Responsive Design, Full-Stack, CRUD Operations, Database Migration, Context API, State Management, Date-fns, Nodemailer, UUID, Vercel, Production Ready, Educational Project, Open Source, Arnob Mahmud
This Doctor Patient Calendar Appointment Management System is a comprehensive, production-ready application that demonstrates modern web development practices. It showcases:
- Modern Tech Stack: Latest versions of Next.js, React, and TypeScript
- Best Practices: Clean code, type safety, and proper architecture
- Security: Authentication, authorization, and data protection
- User Experience: Intuitive interface with multiple views and features
- Scalability: Designed to handle growth and additional features
- Educational Value: Well-documented code perfect for learning
The project is ideal for:
- Healthcare providers and clinics
- Service businesses needing scheduling
- Developers learning full-stack development
- Teams building appointment systems
- Educational purposes and portfolio projects
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! π
Thank you! π












