ab-socks-admin-page
Step-by-step Golden Path and scaffolding checklist for creating or refactoring an admin workstation page in AB-Socks. Enforces strict layer boundaries, controller size limits (<80-120 lines), service
SKILL.md.claude/skills/ab-socks-admin-page/SKILL.md
GitHubAB-Socks Admin Workstation Golden Path
This skill outlines the standard architecture and implementation pattern for creating a new admin workstation page or refactoring an existing bloated one.
1. File Structure & Responsibilities
For any admin section (e.g. discounts):
| File | Purpose | Hard Limits & Invariants |
|---|---|---|
admin/discounts.php |
Front router entry | Exact 2 lines: require_once __DIR__ . '/../app/bootstrap.php'; require_once APP_PATH . '/controllers/admin/discounts.php'; |
app/controllers/admin/discounts.php |
Input orchestration | Max 80–120 lines. Zero SQL queries. Parses $_GET/$_POST, calls service, passes payload to renderView(). |
app/services/DiscountService.php |
Business & Data logic | All SQL queries, transactions, validations. Functions return ['ok' => bool, 'error' => string]. |
views/admin/discounts.php |
Master presentation | Pure HTML. Assembles partials. Zero SQL queries, zero inline <script> > 20 lines. |
views/admin/discounts_partials/ |
Decomposed UI slices | _kpis.php, _toolbar.php, _table.php, _modals.php. |
assets/css/admin-discounts.css |
Workstation layout | Strictly uses :root tokens from admin.css. Zero redundant :root palettes. |
2. Standard Implementation Recipe
Step 1: Controller (app/controllers/admin/discounts.php)
<?php
declare(strict_types=1);
requireAdmin();
$pageTitle = 'مدیریت تخفیفها';
$activeNav = 'discounts';
// 1. Handle POST actions through service
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = (string) ($_POST['action'] ?? '');
if ($action === 'save') {
$result = saveDiscountFromRequest($_POST);
if ($result['ok']) {
setFlash('success', 'تخفیف با موفقیت ذخیره شد.');
redirect('/admin/discounts.php');
}
$error = $result['error'];
}
}
// 2. Fetch presentation data through service
$filters = [
'search' => trim((string) ($_GET['q'] ?? '')),
'status' => trim((string) ($_GET['status'] ?? 'all')),
];
$data = getDiscountsWorkstationData($filters);
// 3. Render pure view
renderView('admin/discounts', array_merge([
'pageTitle' => $pageTitle,
'activeNav' => $activeNav,
'error' => $error ?? null,
], $data));
Step 2: Service (app/services/DiscountService.php)
<?php
declare(strict_types=1);
/**
* Encapsulates all discount and promotion database operations.
*/
function getDiscountsWorkstationData(array $filters): array {
$db = db();
// Prepared SELECT queries...
return [
'items' => $items,
'kpis' => $kpis,
'filters' => $filters,
];
}
function saveDiscountFromRequest(array $input): array {
verifyCsrf();
// Validate fields...
if (empty($input['code'])) {
return ['ok' => false, 'error' => 'کد تخفیف الزامی است.'];
}
// DB write via prepared statement...
return ['ok' => true];
}
Step 3: View & Partials (views/admin/discounts.php)
<?php
// views/admin/discounts.php
?>
<div class="admin-workspace">
<?php require __DIR__ . '/discounts_partials/_header_stats.php'; ?>
<?php require __DIR__ . '/discounts_partials/_kpis.php'; ?>
<?php require __DIR__ . '/discounts_partials/_toolbar.php'; ?>
<?php require __DIR__ . '/discounts_partials/_table.php'; ?>
<?php require __DIR__ . '/discounts_partials/_modals.php'; ?>
</div>
Step 4: Front-end Interactivity (Declarative Only)
- Open modals:
data-ab-modal-open="discountModal" - Live filtering:
data-ab-filter-target="#discountsTable" - Autocomplete:
data-ab-autocomplete="products" - Zero inline
<script>blocks inside the view!
3. Verification & Acceptance Gate
Before finishing, run:
php tools/verify.php
Verify:
- Controller line count <= 80 lines (hard max 120).
- Zero SQL in views.
- Zero duplicate JS helpers created.
- CSS does not define
:root. - Ratchet baseline passes.