A fully functional, responsive school website with a Next.js frontend, Express.js backend, and PostgreSQL database.
- Home - Hero section, stats, programs overview, call-to-action
- About Us - School history, mission/vision/values, leadership team
- Admissions - Process steps, requirements, fee structure
- Contact - Working contact form, school info, map placeholder
- Events & News - Tabbed view of upcoming events and announcements
- Secure JWT-based login for Students/Parents and Administrators
- Role-based route protection
- Token stored in localStorage with auto-redirect
- Overview - Stats cards (students, announcements, events, messages)
- Student Management - Full CRUD (create, read, update, delete)
- Announcements - Create/edit/delete with categories and audience targeting
- Events & Calendar - Manage events with date, time, location
- Messages - View contact form submissions with reply functionality
- Profile - View personal, academic, and parent information
- Announcements - Expandable announcement cards
- Events - View upcoming school events
| Layer | Technology |
|---|---|
| Frontend | Next.js 14, React 18, Tailwind CSS, TypeScript |
| Backend | Node.js, Express.js |
| Database | PostgreSQL |
| Auth | JWT (JSON Web Tokens) + bcrypt |
| Icons | Lucide React |
| HTTP Client | Axios |
crystal-city-school/
βββ frontend/ # Next.js App
β βββ src/
β β βββ app/
β β β βββ page.tsx # Homepage
β β β βββ about/page.tsx # About page
β β β βββ admissions/page.tsx # Admissions page
β β β βββ contact/page.tsx # Contact form
β β β βββ events/page.tsx # Events & News
β β β βββ login/page.tsx # Login page
β β β βββ dashboard/ # Admin Dashboard (protected)
β β β β βββ layout.tsx
β β β β βββ page.tsx # Overview
β β β β βββ students/page.tsx
β β β β βββ announcements/page.tsx
β β β β βββ events/page.tsx
β β β β βββ messages/page.tsx
β β β βββ portal/ # Student Portal (protected)
β β β βββ layout.tsx
β β β βββ page.tsx # Profile
β β β βββ announcements/page.tsx
β β β βββ events/page.tsx
β β βββ components/
β β β βββ Navbar.tsx
β β β βββ Footer.tsx
β β βββ context/
β β β βββ AuthContext.tsx
β β βββ lib/
β β βββ api.ts # API client with Axios
β βββ package.json
β βββ tailwind.config.ts
β βββ .env.example
β
βββ backend/ # Express.js API
β βββ config/
β β βββ database.js # PostgreSQL connection
β β βββ initDb.js # Database initialization script
β βββ controllers/
β β βββ authController.js
β β βββ adminController.js
β β βββ announcementController.js
β β βββ contactController.js
β β βββ eventController.js
β βββ middleware/
β β βββ auth.js # JWT verification + role checks
β βββ routes/
β β βββ authRoutes.js
β β βββ adminRoutes.js
β β βββ announcementRoutes.js
β β βββ contactRoutes.js
β β βββ eventRoutes.js
β βββ utils/
β β βββ generateToken.js
β βββ server.js # Express entry point
β βββ package.json
β βββ .env.example
β
βββ README.md
| Table | Purpose |
|---|---|
users |
Admins, students, parents (with role field) |
announcements |
School announcements with categories |
events |
School events with date/time/location |
contact_messages |
Contact form submissions |
calendar_entries |
Academic calendar items |
- id, email, password, first_name, last_name, role (admin/student/parent)
- phone, address, class_name, admission_number
- parent_name, parent_phone, profile_image, is_active
- Node.js 18+ installed
- PostgreSQL installed and running (or use a cloud database)
# Backend
cd backend
cp .env.example .env # Edit with your database URL
npm install
# Frontend
cd ../frontend
cp .env.example .env.local
npm installEdit backend/.env with your PostgreSQL connection string:
DATABASE_URL=postgresql://username:password@localhost:5432/crystal_city_school
JWT_SECRET=your-super-secret-key-change-this
Create the database:
# In PostgreSQL
createdb crystal_city_school
# Initialize tables + default admin
cd backend
npm run db:init# Terminal 1 - Backend (port 5000)
cd backend
npm run dev
# Terminal 2 - Frontend (port 3000)
cd frontend
npm run dev- Website: http://localhost:3000
- API: http://localhost:5000/api/health
- Admin Login: admin@crystalcity.edu / admin123
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
API health check |
| POST | /api/auth/login |
User login |
| POST | /api/auth/register |
User registration |
| GET | /api/announcements |
Get announcements |
| GET | /api/events |
Get events |
| POST | /api/contact |
Submit contact form |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/auth/me |
Get current user profile |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/stats |
Dashboard statistics |
| GET/POST | /api/admin/students |
List/Create students |
| PUT/DELETE | /api/admin/students/:id |
Update/Delete student |
| GET | /api/admin/messages |
Get contact messages |
| POST | /api/announcements |
Create announcement |
| PUT/DELETE | /api/announcements/:id |
Update/Delete announcement |
| POST | /api/events |
Create event |
| PUT/DELETE | /api/events/:id |
Update/Delete event |
- Go to supabase.com and create an account
- Click "New Project" and choose a name/password
- Go to Settings > Database and copy the Connection String (URI)
- It will look like:
postgresql://postgres:[PASSWORD]@db.[PROJECT].supabase.co:5432/postgres - Use this as your
DATABASE_URLin the backend
- Go to neon.tech and sign up
- Create a new project
- Copy the connection string from the dashboard
- Use as
DATABASE_URL
- Go to mongodb.com/atlas
- Create a free M0 cluster
- Get the connection string
After setting up the database, run the init script:
cd backend
DATABASE_URL="your-connection-string" npm run db:init- Push your backend code to a GitHub repository
- Go to render.com and sign up
- Click "New" > "Web Service"
- Connect your GitHub repo and select the
backendfolder - Settings:
- Build Command:
npm install - Start Command:
node server.js - Environment: Node
- Build Command:
- Add Environment Variables:
DATABASE_URL= your Supabase/Neon connection stringJWT_SECRET= a strong random stringJWT_EXPIRES_IN= 7dNODE_ENV= productionFRONTEND_URL= your Vercel frontend URL (add after deploying frontend)
- Click Deploy
- Note your API URL (e.g.,
https://crystal-city-api.onrender.com)
- Go to railway.app and sign up
- Click "New Project" > "Deploy from GitHub"
- Select your repo, point to
backendfolder - Add the same environment variables as above
- Railway auto-detects Node.js and deploys
- Push your frontend code to GitHub
- Go to vercel.com and sign up with GitHub
- Click "Import Project" and select your repo
- Set the Root Directory to
frontend - Add Environment Variable:
NEXT_PUBLIC_API_URL=https://your-backend-url.onrender.com/api
- Click Deploy
- Your site will be live at
https://your-project.vercel.app
- Go to netlify.com and sign up
- Click "Add new site" > "Import from Git"
- Select your repo, set base directory to
frontend - Build command:
npm run build - Publish directory:
.next - Add environment variable:
NEXT_PUBLIC_API_URL - Deploy
-
Purchase a domain from:
-
Connect to Vercel:
- Go to your Vercel project > Settings > Domains
- Add your domain (e.g.,
crystalcityschool.edu.ng) - Update your domain's DNS:
- Add an A record pointing to
76.76.21.21 - Or add a CNAME pointing to
cname.vercel-dns.com
- Add an A record pointing to
- Wait for DNS propagation (5-30 minutes)
-
Update Backend CORS:
- Add your domain to the
FRONTEND_URLenvironment variable on Render/Railway
- Add your domain to the
- Database initialized with tables (
npm run db:init) - Default admin account works (admin@crystalcity.edu / admin123)
- Backend health check responds:
GET /api/health - Frontend loads and connects to API
- Login works for both admin and student
- Contact form submits successfully
- Admin can create students and announcements
- Change the default admin password immediately!
- Update CORS with your actual frontend domain
| Role | Password | |
|---|---|---|
| Admin | admin@crystalcity.edu | admin123 |
Important: Change the default admin password after first login in production!
- School Name/Info: Edit
Navbar.tsx,Footer.tsx, and page content - Colors: Modify
tailwind.config.tsprimary/accent colors - Logo: Replace the GraduationCap icon with your school logo image
- Content: Update text in page files under
src/app/
DATABASE_URL=postgresql://user:password@host:5432/dbname
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=7d
PORT=5000
NODE_ENV=production
FRONTEND_URL=https://your-frontend.vercel.app
NEXT_PUBLIC_API_URL=https://your-backend.onrender.com/api
This project is built for Crystal City School. All rights reserved.