- Introduction
- Pitch
- Hogwarts
- Live Demo
- MVP
- Roadmap
- Launch Sprint
- PRD
- Get Started
- Localhost
- Architecture
- Structure
- Pattern
- Page
- Layout
- Content
- Types
- Config
- Actions
- Queries
- Authorization
- Validation
- Form
- Table
- Detail
- Card
- Util
- Hooks
- List Params
- Views
- README.md
- ISSUE.md
- Technology Stack
- Database
- File
- CDN Assets
- Entry Points
- Dashboard
- Authentication
- Credentials
- OAuth
- Flow Diagrams
- Multi-Tenancy
- Offline
- Onboarding
- Onboarding Videos
- Add Values
- Admission
- Application
- Attendance
- Compliance
- Profile
- Exams
- Exam Wizard
- Timetable
- Classrooms
- Notifications
- Conference
- LMS (Lumos)
- Finance
- Fee Management
- Invoice
- Wallet
- Salary
- Payroll
- Timesheet
- Expenses
- Budget
- Receipt
- Accounts
- Banking
- Reports
- Dashboard
- Permissions
- Messages
- Integration Flow
- Provision
- AI Document Processing
- Document Intelligence
- Internationalization
- Translation
- Translation Guide
- Icons
- Docs Factory
- Inspiration
- Listings
- Teachers
- Students
- Catalog
- Library
- Contributing
- Code of conduct
- GitHub Workflow
- Database Seeds
- Database Safety
- Test Accounts
- Playwright
- Prettier
- Block Rebound
The dashboard renders one of eight role-specific views — Admin, Developer, Principal, Teacher, Student, Guardian, Accountant, Staff — each composed from the same six unified sections. Routing happens via a switch in content.tsx; section components read the role and adapt content.
Server wrapper + client view
Admin, Student and Teacher follow a two-file split. The <role>.tsx file is a
Server Component that does nothing but fetch — the role's dashboard data, quick
look and weather in one Promise.all, plus the tenant's subdomain — and hand the
result to a <role>-client.tsx view. Every widget the dashboard composes
(MetricCard, ActivityRings, ScheduleItem, the charts) is already a client
component, so keeping the view server-side only fragmented it into dozens of
separate client boundaries.
The client view reads its copy from useDictionary() rather than a dictionary
prop, which keeps section components independently composable. The server file
still needs the dictionary for one thing: the two error fallbacks it can render
before the client view ever mounts.
Parent, Principal, Accountant and Staff have not been split yet.
Section order
Every role renders these six sections in order. Section 2 is title-less.
| # | Section | Component |
|---|---|---|
| 1 | Upcoming + Weather | Upcoming + Weather |
| 2 | Quick Look | QuickLookSection |
| 3 | Quick Actions | QuickActionsSection |
| 4 | Resource Usage | ResourceUsageSection |
| 5 | Invoice History | InvoiceHistorySection |
| 6 | Financial Overview | FinancialOverviewSection |
Role routing
// content.tsx
switch (userRole) {
case "STUDENT":
return <StudentDashboard {...props} />
case "TEACHER":
return <TeacherDashboard {...props} />
case "GUARDIAN":
return <ParentDashboard {...props} />
case "STAFF":
return <StaffDashboard {...props} />
case "ADMIN":
case "DEVELOPER":
return <AdminDashboard {...props} />
case "PRINCIPAL":
return <PrincipalDashboard {...props} />
case "ACCOUNTANT":
return <AccountantDashboard {...props} />
default:
return <DefaultDashboard {...props} />
}Directory layout
src/components/school-dashboard/dashboard/
├── content.tsx, client.tsx, actions.ts, config.ts, validation.ts, types.ts
├── admin.tsx, student.tsx, teacher.tsx, parent.tsx,
│ principal.tsx, accountant.tsx, staff.tsx
├── admin-client.tsx, student-client.tsx, teacher-client.tsx
├── upcoming.tsx, weather.tsx, quick-look-section.tsx,
│ quick-actions.tsx, quick-actions-config.ts,
│ resource-usage-section.tsx, invoice-history-section.tsx,
│ financial-overview-section.tsx, section-heading.tsx
├── metric-card.tsx, activity-rings.tsx, schedule-item.tsx,
│ announcement-card.tsx, progress-card.tsx, chart-card.tsx,
│ empty-state.tsx
├── attendance-chart.tsx, grade-chart.tsx, revenue-chart.tsx,
│ performance-gauge.tsx, weekly-chart.tsx, comparison-chart.tsx
├── header.tsx, loading.tsx, error-boundary.tsx,
│ search-command.tsx, notification-service.tsx,
│ project-switcher.tsx, transactions-list.tsx,
│ upgrade-card.tsx, info-card.tsx, delete-account.tsx
└── settings/ (content.tsx, user-name-form.tsx)
Showcase components (card-*.tsx, chart-*.tsx, stat-*.tsx, dashboard-showcase.tsx) document the widget library.
Upcoming flip card
Role-specific 3D flip card. Front shows primary content; back shows secondary.
| Role | Primary | Secondary |
|---|---|---|
| Student | Assignments due (with overdue) | Next class info |
| Teacher | Next class details | Pending grading, attendance due |
| Parent | Children's pending/overdue | Upcoming events for kids |
| Staff | Urgent tasks, pending requests | Today's priorities |
| Accountant | Pending payments, overdue invoices | Financial deadlines |
| Principal | Critical alerts | Today's priorities, deadlines |
| Admin | System alerts, pending approvals | Urgent notifications |
Weather
Current temp, humidity, wind, rain chance + 6-day forecast. Refresh button. Falls back to mock data.
Resource usage metrics (4 per role)
| Role | Metrics |
|---|---|
| Student | Assignment Completion, Attendance Rate, Grade Average, Library Books |
| Teacher | Classes Taught, Students Count, Pending Grading, Assignments Created |
| Parent | Children Enrolled, Attendance Avg, Pending Tasks, Upcoming Events |
| Staff | Tasks Completed, Requests Processed, Approvals Pending, Efficiency |
| Accountant | Collection Rate, Invoices Processed, Outstanding, Payments Today |
| Principal | School Capacity, Staff Utilization, Budget Usage, Satisfaction |
| Admin | System Usage, Storage, Active Users, API Calls |
Invoice history visibility
| Role | Title | Shows |
|---|---|---|
| Student | Fee History | Own fee invoices |
| Teacher | Expense Claims | Reimbursements |
| Parent | Children's Fees | Fees for all children |
| Staff | Expense Reports | Reimbursements |
| Accountant | Invoice Management | All school invoices |
| Principal | Budget Invoices | Budget-related |
| Admin | System Invoices | All platform invoices |
Financial overview
| Role | View | Chart |
|---|---|---|
| Student | Personal fee status, payment due | No |
| Teacher | Department budget if applicable | No |
| Parent | Combined children's fee status | No |
| Staff | Department budget status | No |
| Accountant | Revenue, collection | Yes — Revenue Collection |
| Principal | School budget | Yes — Monthly Budget |
| Admin | Platform metrics | Yes — Platform Revenue |
Quick actions (4 per role)
| Role | Actions |
|---|---|
| Admin / Developer | Users, Settings, Reports, Analytics |
| Principal | Reports, Staff, Performance, Analytics |
| Teacher | Attendance, Grades, Assignments, Timetable |
| Student | Assignments, Grades, Timetable, Messages |
| Guardian | Children, Grades, Attendance, Messages |
| Accountant | Invoices, Fees, Reports, Receipts |
| Staff | Tasks, Requests, Schedule, Directory |
Server actions
| Action | Returns | Used by |
|---|---|---|
getDashboardSummary() | All metrics aggregated | Admin |
getEnrollmentMetrics() | Student counts | Admin, Principal |
getAttendanceMetrics() | Attendance rates | Admin, Principal, Teacher |
getStaffMetrics() | Teacher counts | Admin, Principal |
getAcademicPerformanceMetrics() | GPA, exams, assignments | Principal, Teacher |
getAnnouncementsMetrics() | Announcement counts | All |
getClassesMetrics() | Class counts, ratios | Admin, Principal |
getRecentActivities() | Activity feed | Admin |
getQuickActionsByRole() | Role-specific actions | All |
getUpcomingDataByRole() | Role-specific upcoming | All |
getResourceUsageByRole() | Role-specific metrics | All |
getInvoicesByRole() | Role-specific invoices | All |
getFinancialOverviewByRole() | Role-specific financials | All |
getTeacherDashboardData() | Teacher-specific | Teacher |
getStudentDashboardData() | Student-specific | Student |
getParentDashboardData() | Parent-specific | Parent |
getPrincipalDashboardData() | Principal-specific | Principal |
Render pattern
Each role dashboard reduces to the same shell.
export async function RoleDashboard({ user, dictionary, locale }: Props) {
const { schoolId } = await getTenantContext()
const school = await db.school.findUnique({ where: { id: schoolId } })
const subdomain = school?.domain ?? ""
return (
<div className="space-y-6">
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
<Upcoming role="ROLE" locale={locale} subdomain={subdomain} />
<Weather />
</div>
<QuickLookSection />
<SectionHeading title="Quick Actions" icon={Zap} />
<QuickActionsSection role="ROLE" locale={locale} subdomain={subdomain} />
<ResourceUsageSection role="ROLE" />
<InvoiceHistorySection role="ROLE" />
<FinancialOverviewSection role="ROLE" />
</div>
)
}Shared widgets
| Widget | File | Purpose |
|---|---|---|
SectionHeading | section-heading.tsx | Unified title with icon, badge, action |
MetricCard | metric-card.tsx | Stat with icon, trend, drill-down |
ActivityRings | activity-rings.tsx | Apple-style concentric rings |
ScheduleItem | schedule-item.tsx | Timetable entry with status |
ProgressCard | progress-card.tsx | Goal with progress bar |
AnnouncementCard | announcement-card.tsx | Priority-styled announcement |
EmptyState | empty-state.tsx | No-data placeholder |
ChartCard | chart-card.tsx | Chart container wrapper |
PerformanceGauge | performance-gauge.tsx | Radial single-metric gauge |
WeeklyActivityChart | weekly-chart.tsx | Weekly bar chart |
ComparisonLineChart | comparison-chart.tsx | Period comparison |
RevenueChart | revenue-chart.tsx | Revenue vs expenses area |