A professional internal request management system built with Native PHP + Bootstrap 5. Clean MVC architecture, smart business logic, REST API, and a polished UI.
| Layer | Technology |
|---|---|
| Backend | Native PHP 8.1+, no framework |
| Database | MySQL 5.7+ / 8.0+ |
| ORM/DB | PDO with prepared statements |
| Frontend | Bootstrap 5.3 (CDN) |
| Icons | Bootstrap Icons 1.11 |
| Fonts | Google Fonts — DM Sans |
crm-system/
├── public/ ← Document root (point your web server here)
│ ├── index.php ← Front controller / entry point
│ └── .htaccess ← URL rewriting rules
│
├── config/
│ ├── app.php ← App constants (name, pagination, thresholds)
│ └── database.php ← PDO singleton connection
│
├── routes/
│ └── web.php ← URI → Controller dispatcher
│
├── app/
│ ├── helpers.php ← Global helper functions
│ ├── models/
│ │ ├── User.php
│ │ └── RequestModel.php
│ ├── controllers/
│ │ ├── AuthController.php
│ │ ├── DashboardController.php
│ │ ├── RequestController.php
│ │ └── ApiController.php
│ └── views/
│ ├── layout/
│ │ ├── header.php ← Topbar + Sidebar + Flash messages
│ │ └── footer.php
│ ├── auth/
│ │ └── login.php
│ ├── dashboard/
│ │ └── index.php
│ ├── requests/
│ │ ├── index.php ← List + filter + search + pagination
│ │ ├── create.php
│ │ └── edit.php
│ └── errors/
│ └── 404.php
│
└── schema.sql ← DB schema + dummy data
git clone <repo-url> crm-system
# or just unzip the foldermysql -u root -p < schema.sqlEdit config/database.php:
define('DB_HOST', 'localhost');
define('DB_NAME', 'crm_system');
define('DB_USER', 'root'); // ← your MySQL username
define('DB_PASS', ''); // ← your MySQL passwordEdit config/app.php:
define('BASE_URL', '');
// If served from subfolder e.g. localhost/crm-system/public:
// define('BASE_URL', '/crm-system/public');Apache — point DocumentRoot to /public, enable mod_rewrite.
Nginx — add this to your server block:
server {
listen 80;
root /path/to/crm-system/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}PHP built-in server (development only):
cd public
php -S localhost:8000Then visit: http://localhost:8000
| Password | Role | |
|---|---|---|
| admin@crm.dev | password123 | Admin |
| budi@crm.dev | password123 | Staff |
| siti@crm.dev | password123 | Staff |
| dani@crm.dev | password123 | Staff |
Returns all requests as JSON.
curl http://localhost:8000/api/requestsResponse:
{
"success": true,
"count": 8,
"data": [
{
"id": 1,
"title": "Pengadaan Laptop Baru",
"description": "...",
"priority": "high",
"status": "urgent",
"created_at": "2026-03-22 10:00:00",
"updated_at": "2026-03-22 10:00:00",
"created_by": "Budi Santoso"
}
]
}Create a new request.
curl -X POST http://localhost:8000/api/requests \
-H "Content-Type: application/json" \
-d '{
"title": "Test Request dari API",
"description": "Deskripsi request melalui REST API endpoint.",
"priority": "medium",
"created_by": 1
}'Response (201):
{
"success": true,
"message": "Request created.",
"id": 9
}The system automatically sets status to urgent when:
strlen(description) > 200 → status = "urgent"
priority === "high" → status = "urgent"
otherwise → status = "pending"
Admins can override this via the Edit form.
- ✅ Login / Logout with PHP sessions
- ✅ Role-based access (admin / staff)
- ✅ Create, list, edit requests
- ✅ Smart auto-urgent logic
- ✅ Dashboard with stats cards
- ✅ Filter by status & priority
- ✅ Search by title
- ✅ Pagination (8 per page)
- ✅ Flash messages (success / error)
- ✅ Form validation
- ✅
timeAgo()timestamps in Bahasa Indonesia - ✅ Color-coded status badges (🟡 Pending / 🔴 Urgent / 🟢 Approved)
- ✅ REST API (GET + POST /api/requests)
- ✅ PDO prepared statements throughout
- ✅ Responsive Bootstrap 5 layout
- PDO prepared statements (no SQL injection)
htmlspecialchars()on all output (no XSS)session_regenerate_id()on login (no session fixation)- HTTPOnly + SameSite session cookies
- Input sanitization via
strip_tags()+trim() - Role-based access control (staff can only edit own requests)