0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • 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
  • 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
Sales & GTM
  • Marketing Brief
  • Sales
  • Go-to-market
  • Marketing
  • Admission — Feature Spotlight
  • Pilot Program
  • Leads
  • Proposal
  • Outreach Templates
  • Case Study
  • Competitors
  • Landing-page teardown
  • Competitor FAQ
  • Business model
  • Shared economy
  • Traction
Fundraising & Ecosystem
  • Get Support
  • Investor Deck
  • Data Room
  • Investors
  • Accelerators
  • Incubators
  • Grants
  • Sponsors
  • Partners
  • Competitions & Hackathons
  • Universities & Training Centers

Profile

PreviousNext

GitHub-inspired multi-role profile — real contribution graph, earned badges, organizations, pinned items, activity feed, and cross-tenant-safe permission masking.

Every school user (student, teacher, parent, staff) gets a GitHub-style profile: a left sidebar (avatar, name, real stat counts, earned badges, organizations) and right content (role tabs, a real contribution graph, pinned items, and an activity feed). One optional catch-all route handles both own-profile and view-by-id.

Everything on the page is real and tenant-scoped. The block was rebuilt (2026-06-15) to remove the previous prototype's fabricated data — hardcoded stats, Math.random() activity, mock "Student of the Year" achievements, and "Chess Club" organizations. When a real source is missing, an honest empty state is shown.

Routes

RoutePageBehaviour
/profileprofile/[[...id]]/page.tsxOwn profile (isOwner=true)
/profile/<id>profile/[[...id]]/page.tsxView by id (User, or wizard entity)

Path: src/app/[lang]/s/[subdomain]/(school-dashboard)/profile/[[...id]]/. The route has loading.tsx (layout-matched skeleton) and error.tsx (locale-aware boundary). id may be a User id, or a wizard-created Student/Teacher id that has no User row (handled by a fallback path).

Architecture

The page calls a single typed read and passes the result down to pure presenter components — no client component fetches or fabricates data.

page.tsx ──> getProfileView(targetId, lang)            // queries.ts (typed, permission-masked)
   │            └─ real stats · badges · organizations · pinned · activity · roleDetail
   └─> ProfileDetailContent ──> ProfileContent (client.tsx)
            ├─ ProfileSidebar   (avatar, stats, badges, organizations, edit entry)
            ├─ PinnedItems      (real pins; owner reorder/remove, persisted)
            ├─ ContributionGraph(SWR → getContributionData; empty grid when none)
            ├─ ContributionActivity (real UserActivity grouped by month)
            ├─ AchievementsGrid    (achievements tab: earned-badge grid)
            └─ Student/Teacher/Parent/Staff dashboards (real subjects/classes/children/orgs)

queries.ts is intentionally not "use server" so callers can wrap it in cache(). Client-callable mutations live in actions.ts ("use server").

Data model

ModelTablePurpose
UserActivityuser_activitiesActivity feed + contribution graph
PinnedItempinned_itemsUp to 6 pinned items per user
ProfileBadgeprofile_badgesEarned badges (awarded by the engine)
OrganizationorganizationsSchool clubs / committees / teams
OrganizationMembershiporganization_membershipsUser ↔ organization, with role
Achievement (existing)achievementsStudent achievement records → badges

GitHub-style fields (bio, website, timezone, pronouns, statusEmoji, statusMessage, socialLinks) live on User.

Deploy note: the migration-of-record prisma/migrations/20260615000000_add_profile_badges_organizations/ was applied to prod on 2026-06-15. To populate the demo school's profile surfaces run pnpm db:seed:single profile-extras then pnpm db:seed:single profile-activity.

Demo data (profile-activity seed)

Every profile section renders from real rows, so the demo needs real rows. The idempotent profile-activity seed (part of seedMain) creates, for the demo school: current-year attendance for the demo student's section (marked by the demo teacher — lights both graphs and earns the attendance badges), an Arabic activity feed for demo accounts + roster/teacher/guardian users, role-appropriate pinned items derived from real subjects/classes/children/ organizations, a parent↔teacher conversation whose parent-sent messages light the parent graph, expense approvals spread across admin/staff/accountant (staff graph), demo-student achievements in distinct categories, and the GitHub-style User fields (status, website, social links, timezone). Badge artwork is served from the CDN at hogwarts/<icon>.png (sources in /public/github, re-upload with npx tsx prisma/scripts/upload-badge-art.ts).

Badge earning engine

badges.ts → recomputeProfileBadges(userId, schoolId, role) derives badges from real signals and reconciles the user's auto-managed badge rows idempotently:

  • perfect_attendance — student with zero absences this year (or a teacher who marked attendance consistently)
  • top_of_class / honor_roll — student merit rank 1 / top 3
  • diligent_educator — teacher who graded/marked ≥ 20 items this year
  • active_contributor — ≥ 20 logged activities this year
  • community_member — belongs to ≥ 1 organization
  • a student's own Achievement records → one badge each (icon by category, level by scope)

Manually-awarded badges (keys outside the catalog / achievement_ prefix) are never touched. Trigger a refresh with the recomputeMyBadges server action.

Permissions (cross-tenant-safe)

getPermissionLevel (detail/permissions.ts) returns OWNER | ADMIN | STAFF | RELATED | PUBLIC. Elevation rules:

  • DEVELOPER is the only role with cross-school access.
  • ADMIN / TEACHER / STAFF / ACCOUNTANT are elevated only when viewerSchoolId === profileSchoolId — otherwise PUBLIC.
  • Same-school students/guardians get RELATED.

getProfileView masks sensitive fields (email, ids) for non-privileged viewers on top of the always-tenant-scoped DB query.

Server actions (actions.ts)

ActionPurpose
updateGitHubProfile(input)Persist name/bio/website/timezone/pronouns/status/social
uploadProfileAvatar(formData)Avatar → User.image and role profilePhotoUrl
getPinnedItems(userId?)Read pins (public-only for other users)
updatePinnedItems(items)Replace own pins (max 6) in a transaction
getContributionData(params)Heatmap data (role lookup schoolId-scoped)
getRecentActivity(userId?, limit)Activity feed (viewer-permission gated)
logUserActivity(input)Append a UserActivity row
recomputeMyBadges()Re-run the earning engine for the caller

Reads are in queries.ts (getProfileView); self-edit entity data in edit-role-actions.ts (getOwnEntity).

Real data, no mock

Every surface is DB-backed:

  • Stats — counts of the user's subjects / classes / students / children
  • Activity — real UserActivity rows (no Math.random())
  • Badges — earned via the engine from real attendance/results/merit
  • Organizations — real memberships (+ a teacher's primary department)
  • Pinned items — real PinnedItem rows; empty state when none

Known issues

  • DB tables deploy-pending (see Deploy note).
  • Adding a new pinned item from the UI is not yet wired (display + reorder + remove are).
  • profile-flows.spec.ts predates the rebuild — selectors + a cross-school isolation case are a follow-up.

See also

  • Tests: src/tests/school-dashboard/profile/
  • Block records: src/components/school-dashboard/profile/{README,ISSUE,CLAUDE}.md
ComplianceExams

On This Page

RoutesArchitectureData modelDemo data (profile-activity seed)Badge earning enginePermissions (cross-tenant-safe)Server actions (actions.ts)Real data, no mockKnown issuesSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.