This repository currently contains the frontend application for IEEE Student Branch operations (finance-oriented dashboard + society operations modules), plus local developer tooling at the root.
The app is built with React + TypeScript + Vite + Tailwind CSS and is already wired to connect to a backend that follows a clear API contract.
The frontend provides one platform for:
- Authentication (login/register/change password)
- Profile management
- Society management
- Event management
- Project management
- Report management
- Announcements
- Community hub (REST + realtime socket updates)
- Admin user listing (permission-gated)
The app is designed around RBAC (role/permission-driven UI visibility) and uses a shared API layer so backend teams can connect without rewriting UI logic.
SB-APP/
├─ package.json # root: Husky + lint-staged configuration
├─ .husky/pre-commit # secret checks + optional trufflehog + lint-staged
├─ frontend/
│ ├─ package.json # frontend scripts and dependencies
│ ├─ .env.example # VITE_API_BASE_URL
│ ├─ vite.config.ts # dev server + /api and /socket.io proxy
│ ├─ tailwind.config.js # design tokens and theme mapping
│ ├─ src/
│ │ ├─ main.tsx # app bootstrap
│ │ ├─ routes.tsx # complete route map
│ │ ├─ lib/
│ │ │ ├─ api.ts # Axios instance + auth interceptors
│ │ │ ├─ socket.ts # socket.io client singleton
│ │ │ └─ permissions.ts # access-level comparison helper
│ │ ├─ store/
│ │ │ ├─ authStore.ts # token/user/permissions state
│ │ │ └─ themeStore.ts # dark mode persistence
│ │ ├─ services/ # API service modules by domain
│ │ ├─ pages/ # route pages (auth, modules, admin, landing)
│ │ ├─ components/
│ │ │ ├─ auth/ # ProtectedRoute
│ │ │ ├─ layout/ # Layout/Header/Sidebar/AuthLayout
│ │ │ ├─ ui/ # DataTable/Button/Pagination/etc.
│ │ │ └─ PermissionGate.tsx # inline RBAC-gated rendering
│ │ ├─ hooks/ # pagination + permission helpers
│ │ ├─ types/ # API + model contracts
│ │ └─ index.css # global styles + animations + CSS vars
│ ├─ mock/
│ │ ├─ mock-server.js # json-server + custom routes + socket.io
│ │ └─ db.json # mock data
│ └─ public/
│ └─ logos/ # society logos used by dashboard marquee
└─ logos/ # repository-level static assets
Note: root
package.jsonreferencesbackend/**/*in lint-staged, but this clone currently contains only frontend code.
- Node.js 18+
- npm
From repository root:
npm installFrom frontend:
cd frontend
npm installCreate frontend/.env:
VITE_API_BASE_URL=http://localhost:5000/api/v1cd frontend
npm run devFrontend runs on http://localhost:5173.
cd frontend
npm run mockMock server runs on http://localhost:5000 and serves API at /api/v1.
npm run prepare→ installs Husky hooks
npm run dev→ Vite dev servernpm run build→ TypeScript build + Vite production buildnpm run lint→ ESLintnpm run typecheck→ TS noEmit checknpm run test:unit→ placeholder (No tests yet)npm run preview→ preview built appnpm run mock→ start mock API/socket server
- blocks staged secret-like files (
.env,.pem,.key, etc.) - runs TruffleHog if available locally
- runs npm audit if package files changed
- runs lint-staged formatting
src/main.tsx:- initializes theme before mount (
useThemeStore.getState().initTheme()) - wraps app in
ErrorBoundary - mounts router via
RouterProvider - mounts
react-hot-toasttoaster
- initializes theme before mount (
Routes are split into three layers:
- Public landing route (
/) - Auth layout routes (
/login,/register) - Authenticated app layout routes under
<Layout/>+<ProtectedRoute/>
Authenticated route groups:
- General protected:
/dashboard/profile/change-password- CRUD-style routes for societies/events/projects/reports/announcements
/community
- Admin-protected:
/admin/usersrequires moduleusersand actionadmin
Stores:
tokenuserpermissionsisAuthenticated
Persists state to localStorage and rehydrates using safe JSON parse.
Key methods:
login(email, password)register(name, email, password)logout()fetchPermissions()from/user/permissionsgetAccessLevel(module)updateUserProfile(data)
Stores and persists darkMode; toggles .dark class on <html>.
- base URL from
VITE_API_BASE_URL(fallback/api/v1) - request interceptor attaches the auth token in the HTTP authorization header when available
- response interceptor handles
401by clearing token and redirecting to/login
Each domain owns one service wrapper with typed methods. Examples:
authService(/auth/login,/auth/register,/auth/change-password)societyService(/societies)eventService(/events)projectService(/projects)reportService(/reports)announcementService(/announcements)communityService(/community/messages)userService(/users)
Permission data shape (types/models.ts):
{ module: string; action: string; accessLevel: 'none' | 'read' | 'write' | 'admin' | 'superadmin' }UI enforcement happens in three places:
- Route-level:
ProtectedRoute - Component-level:
PermissionGate - Navigation-level:
Sidebarhides modules user cannot read
Action-to-level mapping treats create like write, and delete like admin.
- Tailwind tokens are mapped to CSS variables (
index.css+tailwind.config.js) - Dark mode switches token values by toggling
.dark - Reusable UI blocks:
ButtonDataTable(sortable headers)SearchInput(debounced)PaginationLoadingSpinnerErrorBoundary
HTML content from API is sanitized before injection:
ReportDetailPageusesDOMPurify.sanitize(...)CommunityPageusesDOMPurify.sanitize(...)
This is critical for backend teams returning rich text fields (report.content, message content).
lib/socket.ts manages a socket.io singleton.
- Host derived from
VITE_API_BASE_URL(removes/api/v1) - token sent through
socket.auth - reconnection is enabled
Community module:
- initial fetch via REST (
GET /community/messages) - live updates via
community:messagesocket event
Frontend expects:
{
"success": true,
"data": ...,
"meta": { "page": 1, "limit": 20, "totalItems": 100, "totalPages": 5 }
}For errors:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable message",
"details": {}
}
}Supported ErrorCode values are defined in src/types/api.ts.
List endpoints should support:
- query params:
page,limit - response includes
metawithpage,limit,totalItems,totalPages
Request:
{ "email": "...", "password": "..." }Response data:
{
"token": "jwt-or-token",
"user": { "id": "...", "name": "...", "email": "..." }
}Request:
{ "name": "...", "email": "...", "password": "..." }Response shape same as login.
Request:
{ "currentPassword": "...", "newPassword": "..." }Response:
{ "success": true, "data": null }Response:
{
"success": true,
"data": {
"permissions": [
{ "module": "societies", "action": "view", "accessLevel": "admin" }
]
}
}Important:
modulenames must align with frontend module keys (societies,events,projects,reports,announcements,community,users).
- Users:
GET /users,GET /users/:id,PATCH /users/:id - Societies:
GET /societies,GET /societies/:id,POST /societies,PATCH /societies/:id - Events:
GET /events,GET /events/:id,POST /events,PATCH /events/:id,DELETE /events/:id - Projects:
GET /projects,GET /projects/:id,POST /projects,PATCH /projects/:id,DELETE /projects/:id - Reports:
GET /reports,GET /reports/:id,POST /reports,PATCH /reports/:id,DELETE /reports/:id - Announcements:
GET /announcements,GET /announcements/:id,POST /announcements,PATCH /announcements/:id,DELETE /announcements/:id - Community:
GET /community/messages,POST /community/messages
Core fields consumed by screens are in src/types/models.ts.
Backend should preserve these names for seamless integration (or coordinate a typed migration).
- Implement endpoints under
/api/v1(or updateVITE_API_BASE_URLaccordingly). - Return the exact response envelope (
success,data, optionalmeta). - Implement permission endpoint with correct module keys.
- Ensure the backend reads the auth token from the HTTP authorization header.
- For community realtime, emit
community:messagewithCommunityMessagepayload. - Verify CORS and credentials for local dev (
localhost:5173). - Point frontend
.envto real backend URL. - Run frontend and validate:
- login -> permissions fetch -> sidebar visibility
- list pages pagination/search/sort
- create/edit/delete flows
- community live updates
When adding a new module (example: budgets), follow this existing pattern:
-
Type contracts
- add model in
src/types/models.ts - add API typing if needed in
src/types/api.ts
- add model in
-
Service layer
- create
src/services/budgets.ts - include list/detail/create/update/delete methods
- create
-
Pages
- create list/detail/form pages under
src/pages/budgets/
- create list/detail/form pages under
-
Routing
- register routes in
src/routes.tsx - wrap protected routes using
ProtectedRoute/module requirements when needed
- register routes in
-
Navigation and RBAC
- add sidebar item in
Sidebar.tsx - use
PermissionGatefor action-level controls
- add sidebar item in
-
UI consistency
- reuse existing
DataTable,SearchInput,Pagination,Button,LoadingSpinner - follow tokenized classes (
text-ink,bg-surface, etc.)
- reuse existing
-
Validation/security
- use
zod+react-hook-formin forms - sanitize any HTML before
dangerouslySetInnerHTML
- use
-
Realtime (optional)
- extend
lib/socket.tsevent handling if module needs live updates
- extend
frontend/mock/mock-server.js provides:
- json-server data source (
db.json) - auth mock endpoints
- permission mock endpoint
- pagination wrappers
- REST CRUD mapping to collections
- socket.io server
- broadcast on community message creation
Resource map implemented:
/api/v1/users->users/api/v1/societies->societies/api/v1/events->events/api/v1/projects->projects/api/v1/reports->reports/api/v1/announcements->announcements/api/v1/community/messages->communityMessages
| Route | Purpose | Protection |
|---|---|---|
/ |
Marketing/landing | Public |
/login, /register |
Auth entry | Public (AuthLayout) |
/dashboard |
Overview + cards + carousel + marquee | Auth required |
/profile |
User profile | Auth required |
/change-password |
Password change | Auth required |
/societies* |
Society CRUD/list/detail | Auth required + action-based buttons |
/events* |
Event CRUD/list/detail | Auth required + action-based buttons |
/projects* |
Project CRUD/list/detail | Auth required + action-based buttons |
/reports* |
Report CRUD/list/detail | Auth required + action-based buttons |
/announcements* |
Announcement CRUD/list/detail | Auth required + action-based buttons |
/community |
Chat/community hub | Auth required |
/admin/users |
User management | Requires users + admin |
- 401 handling is global: any unauthorized API response logs user out and redirects to
/login. - Permissions drive UI visibility: missing or wrong module keys hide routes/actions.
- Pagination meta is required on list pages: without it, paging controls break.
- Rich text must be trusted+sanitized: frontend sanitizes, but backend should still validate/stored content safely.
- Socket URL is derived from API base URL: keep API base predictable (
.../api/v1).
-
/api/v1base reachable from frontend - Auth endpoints return expected
token + user -
/user/permissionsreturns correct module names and levels - All list endpoints return
data[] + meta - CRUD endpoints align with service method paths
- CORS allows frontend host(s)
-
community:messagesocket emission implemented - Error envelope (
success:false/error.code/message) standardized - Validate production behavior with
npm run buildandnpm run preview
- No production backend code in this repository clone yet.
- Unit tests are currently placeholder (
test:unit). - Some dashboard comments indicate future Team 2/Team 4 integration for richer backend-driven data fields (e.g., logo URLs).
# terminal 1
cd frontend
cp .env.example .env
npm install
npm run mock
# terminal 2
cd frontend
npm run devOpen http://localhost:5173.