Citizen opinion & government feedback portal for Bangladesh.
Jonomot connects citizens with government ministries and private organizations. Citizens publish civic reports, ministries respond from a dedicated inbox, companies and ministries run surveys with reward points, and admins moderate the platform.
- Features
- Tech stack
- Architecture
- Repository layout
- Prerequisites
- Local setup
- Environment variables
- Demo accounts
- NID verification (mock)
- Roles & permissions
- Frontend routes
- API overview
- Database
- Internationalization
- Storage & media
- Testing
- Development notes
- Documentation
- Register / login and manage profile (avatar, language, location)
- NID verification (mock service) before publishing or voting
- Browse and publish civic posts (problem, solution, policy, advice) with optional images
- Like, dislike, save, comment, and report posts
- Earn points for survey participation (
points_ledger) - Vote on ministry/company polls that match demographic filters (age, division)
- Dedicated inbox of posts tagged to that ministry
- Star important posts (
post_ministry.is_starred) - Post official comments on tagged reports
- Create and manage surveys (polls) with optional filters and reward points
- Verified organization profiles (
company_details) - Create surveys with reward points for citizen voters
- View poll analytics and results
- Approve pending ministries and companies
- Review pending reports and flagged posts (posts accumulate flags after enough reports)
- Restore moderated posts
- Platform stats dashboard
- Create/manage polls and tag organizations
- Bangla / English UI with persisted preference
- Light / dark theme
- Bangladesh division map on auth
- Toast notifications and role-gated navigation
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 8, React Router 7, Axios, Recharts |
| Backend | Laravel 12 (PHP 8.2+), Laravel Sanctum |
| Database | MySQL 8 (recommended) or SQLite for quick local runs |
| Auth | Bearer API tokens (Sanctum) |
| Storage | Laravel public disk (storage/app/public) via storage:link |
Browser (React SPA :5173)
│ JSON + Bearer token
▼
Laravel API (:8000/api)
│ Sanctum + role + NID middleware
▼
MySQL ◄──► File storage (/storage/...)
- SPA talks only to
/api/*(no Blade UI for the product surface). - Auth: login/register returns a Sanctum personal access token; the frontend stores it as
jonomot_tokenand sendsAuthorization: Bearer …. - Authorization: custom
rolemiddleware andnid_verifiedgate for civic write actions. - Input hardening: sanitize middleware, throttles (login, posts, poll votes), security headers.
Jonomot/
├── backend/ # Laravel 12 API
│ ├── app/
│ │ ├── Http/Controllers/Api/
│ │ ├── Http/Middleware/ # Role, NID, Sanitize, SecurityHeaders
│ │ ├── Models/
│ │ └── Services/ # e.g. NidVerificationService
│ ├── database/
│ │ ├── migrations/
│ │ └── seeders/ # Demo users, posts, polls, geo content
│ ├── routes/api.php
│ └── storage/app/public/ # Linked public files (posts, avatars)
├── frontend/ # React + Vite SPA
│ ├── src/
│ │ ├── api/client.js
│ │ ├── components/
│ │ ├── context/ # Auth, Language, Toast
│ │ ├── i18n/
│ │ ├── utils/
│ │ └── views/ # Feed, Polls, Inbox, Admin, Profile, Login
│ └── package.json
├── docs/ # Schema / ER diagrams
│ ├── jonomot-schema.png
│ ├── jonomot-er.png
│ └── jonomot-er.mmd
└── README.md
- PHP 8.2+ with extensions commonly required by Laravel (
pdo,mbstring,openssl,tokenizer,xml,ctype,json,fileinfo) - Composer 2.x
- Node.js 20+ and npm
- MySQL 8.x (or MariaDB) — or use SQLite via
.envfor a minimal setup
Create an empty MySQL database (example name: jonomot_db):
CREATE DATABASE jonomot_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;cd backend
composer install
copy .env.example .env # Windows
# cp .env.example .env # macOS / Linux
php artisan key:generateEdit .env (see Environment variables), then:
php artisan migrate:fresh --seed
php artisan storage:link
php artisan serveAPI default: http://127.0.0.1:8000 (routes under /api).
cd frontend
npm install
npm run devApp default: http://127.0.0.1:5173 (or http://localhost:5173).
Set VITE_API_URL if the API is not at http://localhost:8000/api.
| Step | Command / URL |
|---|---|
| Migrate + seed | php artisan migrate:fresh --seed |
| Public storage | php artisan storage:link |
| API | php artisan serve → :8000 |
| SPA | npm run dev → :5173 |
| Open app | http://localhost:5173 |
| Variable | Purpose | Example |
|---|---|---|
APP_KEY |
Laravel encryption key | from key:generate |
APP_URL |
Backend base URL | http://127.0.0.1:8000 |
FRONTEND_URL |
SPA origin (CORS / redirects / CSP) | http://127.0.0.1:5173 |
DB_CONNECTION |
Driver | mysql (or sqlite) |
DB_HOST / DB_PORT |
MySQL host | 127.0.0.1 / 3306 |
DB_DATABASE |
Database name | jonomot_db |
DB_USERNAME / DB_PASSWORD |
Credentials | root / (empty for XAMPP) |
SESSION_DRIVER |
Session store | database |
QUEUE_CONNECTION |
Queue | database or sync |
FILESYSTEM_DISK |
Default disk | local |
For MySQL, a typical block looks like:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=jonomot_db
DB_USERNAME=root
DB_PASSWORD=
FRONTEND_URL=http://127.0.0.1:5173
APP_URL=http://127.0.0.1:8000Optional in frontend/.env:
VITE_API_URL=http://127.0.0.1:8000/apiIf unset, the client defaults to http://localhost:8000/api.
Seeded after migrate:fresh --seed:
| Role | Password | Notes | |
|---|---|---|---|
| Admin | admin@jonomot.gov.bd |
admin123 |
Full moderation |
| Citizen (verified) | rahim@gmail.com |
user123 |
Can post & vote |
| Citizen (unverified) | imran@gmail.com |
user123 |
Must verify NID first |
| Ministry | mortb@jonomot.gov.bd (and other *@jonomot.gov.bd) |
govt123 |
Inbox + surveys |
| Company | pran@rflgroup.com |
company123 |
Surveys |
| Company | beximco@pharmabd.com |
company123 |
Surveys |
| Pending ministry | molgd@pending.jonomot.gov.bd |
govt123 |
Awaiting admin approval |
Additional citizens and ministries are created by seeders for a richer feed.
Citizens must pass NID verification before creating posts or voting on polls. Verification is handled by NidVerificationService (demo register — not a live NID API).
Example unused / demo pairs (name must match the register entry):
| NID | Name (BN or EN) |
|---|---|
1234567890 |
করিম বেঞ্জ / Karim Benz |
1111111111 |
সাব্বির রহমান / Sabbir Rahman |
2222222222 |
ফাহিম ফয়সাল / Fahim Faisal |
7777777777 |
তামিম ইকবাল / Tamim Iqbal |
Already-seeded citizens (e.g. Rahim 5544332211, Imran 1122334455) use those NIDs in the mock register.
Endpoint: POST /api/nid-verify (authenticated).
| Capability | Citizen | Ministry | Company | Admin |
|---|---|---|---|---|
| Browse posts / polls | ✓ | ✓ | ✓ | ✓ |
| Publish posts / comment / like / report | ✓* | ✓* | ✓* | ✓* |
| Vote on polls | ✓* | — | — | — |
| Ministry inbox + star | — | ✓ | — | — |
| Create / close polls | — | ✓ | ✓ | ✓ |
| Approve orgs + moderate | — | — | — | ✓ |
* Requires Sanctum auth; for citizens, also requires is_nid_verified.
Middleware:
auth:sanctum— valid Bearer tokenrole:…—RoleMiddlewarenid_verified—NidVerifiedMiddleware(citizens only)
| Path | View | Access |
|---|---|---|
/ |
Feed (posts, compose, interactions) | Public read; write needs auth |
/login |
Login / register | Guest |
/polls |
Polls hub (list, create, analytics, vote) | Auth for create/vote |
/profile |
Profile, NID, language, activity | Auth |
/ministry/inbox |
Ministry inbox | ministry |
/admin/dashboard |
Admin panel | admin |
Theme is stored in localStorage as jonomot_theme (light | dark).
Base path: /api
Auth header: Authorization: Bearer {token}
POST /register,POST /loginGET /categories,GET /ministriesGET /posts,GET /posts/{id}GET /holidaysGET /geo/bangladesh,GET /geo/locate,GET /geo/reverse
POST /logout,GET|PUT /profile,POST /profile/avatarPOST /nid-verify,GET /activityPOST /posts, likes/dislikes/report/comments/saveGET /polls,GET /polls/{id}/results, analytics routesPOST /polls/{id}/vote(citizen + NID)GET /ministry/inbox,POST /posts/{id}/star(ministry)POST /polls,POST /polls/{id}/close,DELETE /polls/{id}(ministry/company/admin)- Admin:
/admin/pending-*,/admin/reports,/admin/flagged-posts, resolve/dismiss/restore,/admin/stats
Throttles apply on login, post creation, and poll voting.
Core tables (see also docs/jonomot-schema.png and docs/jonomot-er.mmd):
| Table | Purpose |
|---|---|
users |
All roles; location, NID, points, language |
ministry_details / company_details |
Org profiles + verification flags |
categories |
Post categories |
posts |
Civic posts + image path + status |
post_ministry |
Post ↔ ministry tags + is_starred |
comments |
Threaded via parent_id |
post_votes / post_saves / post_reports |
Interactions |
user_interests / user_ministry_interests |
Citizen preferences |
polls / poll_options / poll_responses |
Surveys + filters + rewards |
points_ledger |
Point credits/debits |
personal_access_tokens |
Sanctum tokens |
Regenerate schema diagram:
node docs/build-schema.mjs- UI strings:
frontend/src/i18n/translations.js(bn/en) - Runtime language:
LanguageContext(+ userpreferred_language) - Seed / domain labels:
frontend/src/utils/localeLabels.js(categories, ministries,name_en, holidays) - Default product language is Bangla; users can switch from the top bar or Profile
- Post images:
storage/app/public/posts/(served as/storage/posts/...) - Avatars: under the public disk similarly
- Run
php artisan storage:linkonce so the web server can serve files - Frontend builds media URLs via
frontend/src/utils/media.js(mediaUrl)
Seed data may include contextual Bangladesh-themed post images under the seeder assets folder.
Backend (PHPUnit):
cd backend
php artisan testFrontend lint:
cd frontend
npm run lintPlaywright is available as a frontend dev dependency for browser tests if you add specs under the frontend project.
- CORS / cookies: API is token-based; set
FRONTEND_URLto the exact SPA origin you use (localhostvs127.0.0.1matter for some browsers). - Polls: Organization surveys go live with
payment_status: freeand a configuredreward_pointsvalue; citizens who match filters earn those points on vote. - Flagging: Individual
post_reportsare reviewed in admin; posts become flagged after repeated reports on the same post (see admin “flagged posts” vs “pending reports”). - Division matching: Poll audience filters normalize Bangla/English division names so citizens are not incorrectly excluded.
- Do not commit
.env,vendor/,node_modules/, or localstorageuploads with secrets.
Useful artisan commands:
php artisan migrate:fresh --seed # reset DB + demo data
php artisan storage:link # public disk symlink
php artisan route:list --path=api # inspect API routes
php artisan test| File | Description |
|---|---|
docs/jonomot-schema.png |
Relational schema diagram (table boxes + FK arrows) |
docs/jonomot-schema.svg |
Vector version of the schema |
docs/jonomot-er.mmd |
Mermaid crow’s-foot ER source |
docs/build-schema.mjs |
Regenerates schema PNG/SVG |
Application code follows the licenses of included frameworks (Laravel / MIT-style frontend tooling). Add a project-specific license file if you distribute Jonomot publicly.