nhance/app/Controllers/Docs/DocsController.php
2026-05-22 15:01:14 +05:30

812 lines
46 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Controllers\Docs;
use App\Controllers\BaseController;
use CodeIgniter\Exceptions\PageNotFoundException;
/**
* DocsController
* app/Controllers/Docs/DocsController.php
*
* Handles all developer documentation pages.
* Each page only needs to define its own content — layout partials are assembled here.
*
* Route setup (app/Config/Routes.php):
* $routes->get('docs', 'Docs\DocsController::index');
* $routes->get('docs/(:segment)', 'Docs\DocsController::page/$1');
*/
class DocsController extends BaseController
{
// ─── APP-LEVEL DEFAULTS ───────────────────────────────────────────────────
// Change these once here; they propagate to every page automatically.
protected string $appName = 'Nhance PAM';
protected string $appVersion = 'v1.1.0';
// ─── SIDEBAR NAV ──────────────────────────────────────────────────────────
// Add / remove pages here. 'id' must match the key used in page configs below.
protected array $nav = [
[
'label' => 'Getting Started',
'items' => [
['id' => 'introduction', 'label' => 'Introduction', 'url' => 'docs/introduction'],
['id' => 'installation', 'label' => 'Installation', 'url' => 'docs/installation'],
['id' => 'configuration', 'label' => 'Configuration', 'url' => 'docs/configuration'],
['id' => 'env-setup', 'label' => 'Environment Setup', 'url' => 'docs/env-setup'],
],
],
[
'label' => 'Architecture',
'items' => [
['id' => 'project-structure', 'label' => 'Project Structure', 'url' => 'docs/project-structure'],
['id' => 'routing', 'label' => 'Routing', 'url' => 'docs/routing'],
['id' => 'controllers', 'label' => 'Controllers', 'url' => 'docs/controllers'],
['id' => 'models', 'label' => 'Models', 'url' => 'docs/models'],
['id' => 'services', 'label' => 'Services', 'url' => 'docs/services'],
['id' => 'helpers', 'label' => 'Helpers', 'url' => 'docs/helpers'],
],
],
[
'label' => 'Features',
'items' => [
['id' => 'authentication', 'label' => 'Authentication', 'url' => 'docs/authentication'],
['id' => 'acl', 'label' => 'ACL / Access Control','url' => 'docs/acl'],
['id' => 'input-security', 'label' => 'Input Security Guard','url' => 'docs/input-security'],
['id' => 'file-uploads', 'label' => 'File Upload Guard', 'url' => 'docs/file-uploads'],
['id' => 'background-jobs', 'label' => 'Background Jobs', 'url' => 'docs/background-jobs'],
['id' => 'visit-onboard', 'label' => 'Visit onboard', 'url' => 'docs/visit-onboard'],
['id' => 'visit-offboard', 'label' => 'Visit offboard', 'url' => 'docs/visit-offboard'],
['id' => 'notifications', 'label' => 'Email / Notifications', 'url' => 'docs/notifications'],
['id' => 'api-rate-limiter', 'label' => 'API Rate Limiter', 'url' => 'docs/api-rate-limiter'],
['id' => 'tpa-recon', 'label' => 'TPA Recon', 'url' => 'docs/tpa-recon'],
['id' => 'eb-rack-rate-config', 'label' => 'EB rack rate config', 'url' => 'docs/eb-rack-rate-config'],
['id' => 'eb-rack-rate-calculation', 'label' => 'EB rack rate calculation', 'url' => 'docs/eb-rack-rate-calculation'],
['id' => 'bds-insurer-statement', 'label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'],
['id' => 'bds-commission', 'label' => 'BDS commission', 'url' => 'docs/bds-commission'],
['id' => 'inception', 'label' => 'Inception', 'url' => 'docs/inception'],
['id' => 'deletion', 'label' => 'Deletion', 'url' => 'docs/deletion'],
['id' => 'correction', 'label' => 'Correction', 'url' => 'docs/correction'],
['id' => 'si-enhancement', 'label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'],
['id' => 'non-eb-opportunities', 'label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'],
['id' => 'non-eb-claims', 'label' => 'Non-EB Claims', 'url' => 'docs/non-eb-claims'],
],
],
[
'label' => 'API Reference',
'items' => [
['id' => 'endpoints', 'label' => 'Endpoints', 'url' => 'docs/endpoints'],
['id' => 'request-response', 'label' => 'Request / Response', 'url' => 'docs/request-response'],
['id' => 'error-codes', 'label' => 'Error Codes', 'url' => 'docs/error-codes'],
],
],
[
'label' => 'DevOps',
'items' => [
['id' => 'deployment', 'label' => 'Deployment', 'url' => 'docs/deployment'],
['id' => 'cicd', 'label' => 'CI/CD Pipeline', 'url' => 'docs/cicd'],
['id' => 's3-cloudfront', 'label' => 'S3 & CloudFront', 'url' => 'docs/s3-cloudfront'],
],
],
[
'label' => 'Reference',
'items' => [
['id' => 'changelog', 'label' => 'Changelog', 'url' => 'docs/changelog'],
['id' => 'contributing', 'label' => 'Contributing', 'url' => 'docs/contributing'],
],
],
];
// ─── PAGE REGISTRY ────────────────────────────────────────────────────────
// One entry per docs page.
// 'view' — path inside app/Views/ (without .php)
// Other keys are passed straight to the partials — add/remove as needed.
protected array $pages = [
'introduction' => [
'view' => 'docs/introduction',
'title' => 'Introduction',
'breadcrumb' => 'Getting Started',
'last_updated' => 'May 2025',
'author' => 'Core Team',
'read_time' => '3 min read',
'toc' => [
['label' => 'What is MyApp?', 'href' => '#what-is-myapp'],
['label' => 'Tech stack', 'href' => '#tech-stack'],
['label' => 'Conventions', 'href' => '#conventions'],
],
'prev' => null,
'next' => ['label' => 'Installation', 'url' => 'docs/installation'],
],
'installation' => [
'view' => 'docs/installation',
'title' => 'Installation',
'breadcrumb' => 'Getting Started',
'last_updated' => 'May 2025',
'author' => 'Core Team',
'read_time' => '5 min read',
'toc' => [
['label' => 'Requirements', 'href' => '#requirements'],
['label' => 'Steps', 'href' => '#steps'],
['label' => 'Clone repo', 'href' => '#clone', 'level' => 'h3'],
['label' => 'Run migrations', 'href' => '#migrate', 'level' => 'h3'],
['label' => 'Configuration', 'href' => '#configuration'],
['label' => 'Sample flowchart', 'href' => '#sample-flowchart'],
],
'prev' => ['label' => 'Introduction', 'url' => 'docs/introduction'],
'next' => ['label' => 'Configuration', 'url' => 'docs/configuration'],
],
'configuration' => [
'view' => 'docs/configuration',
'title' => 'Configuration',
'breadcrumb' => 'Getting Started',
'last_updated' => 'May 2025',
'author' => 'Core Team',
'read_time' => '4 min read',
'toc' => [
['label' => 'Environment file', 'href' => '#env-file'],
['label' => 'Database', 'href' => '#database'],
['label' => 'Mail', 'href' => '#mail'],
['label' => 'File storage', 'href' => '#storage'],
],
'prev' => ['label' => 'Installation', 'url' => 'docs/installation'],
'next' => ['label' => 'Environment Setup','url' => 'docs/env-setup'],
],
'acl' => [
'view' => 'docs/acl',
'title' => 'ACL / Access Control',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '9 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Where it is wired', 'href' => '#where-it-is-wired'],
['label' => 'Rule format', 'href' => '#rule-format'],
['label' => 'Matching behavior', 'href' => '#matching-behavior'],
['label' => 'Auth context', 'href' => '#auth-context'],
['label' => 'Allow and deny flow', 'href' => '#allow-and-deny-flow'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Examples', 'href' => '#examples'],
['label' => 'Do and dont', 'href' => '#do-and-dont'],
['label' => 'Common pitfalls', 'href' => '#common-pitfalls'],
],
'prev' => null,
'next' => ['label' => 'Input Security Guard', 'url' => 'docs/input-security'],
],
'input-security' => [
'view' => 'docs/input-security',
'title' => 'Input Security Guard',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Where it runs', 'href' => '#where-it-runs'],
['label' => 'What it checks', 'href' => '#what-it-checks'],
['label' => 'Canonicalization', 'href' => '#canonicalization'],
['label' => 'Block behavior', 'href' => '#block-behavior'],
['label' => 'Filter exceptions', 'href' => '#filter-exceptions'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#common-pitfalls'],
],
'prev' => ['label' => 'ACL / Access Control', 'url' => 'docs/acl'],
'next' => ['label' => 'File Upload Guard', 'url' => 'docs/file-uploads'],
],
'file-uploads' => [
'view' => 'docs/file-uploads',
'title' => 'File Upload Guard',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'When it runs', 'href' => '#when-it-runs'],
['label' => 'Allowed file types', 'href' => '#allowed-file-types'],
['label' => 'Blocked extensions', 'href' => '#blocked-extensions'],
['label' => 'Validation flow', 'href' => '#validation-flow'],
['label' => 'Magic bytes check', 'href' => '#magic-bytes-check'],
['label' => 'Route coverage', 'href' => '#route-coverage'],
['label' => 'Blocked response', 'href' => '#blocked-response'],
['label' => 'Operational notes', 'href' => '#operational-notes'],
],
'prev' => ['label' => 'Input Security Guard', 'url' => 'docs/input-security'],
'next' => ['label' => 'Background Jobs', 'url' => 'docs/background-jobs'],
],
'background-jobs' => [
'view' => 'docs/background-jobs',
'title' => 'Background Jobs',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Queueing jobs', 'href' => '#queueing-jobs'],
['label' => 'Worker lifecycle', 'href' => '#worker-lifecycle'],
['label' => 'Handler registry', 'href' => '#handler-registry'],
['label' => 'Sample handlers', 'href' => '#sample-handlers'],
['label' => 'Status lifecycle', 'href' => '#status-lifecycle'],
['label' => 'Failure behavior', 'href' => '#failure-behavior'],
['label' => 'Running via CLI', 'href' => '#running-via-cli'],
['label' => 'Live runner script', 'href' => '#live-runner-script'],
['label' => 'Systemd service', 'href' => '#systemd-service'],
['label' => 'Permissions setup', 'href' => '#permissions-setup'],
['label' => 'Checking status', 'href' => '#checking-status'],
['label' => 'Adding a new handler','href' => '#adding-a-new-handler'],
],
'prev' => ['label' => 'File Upload Guard','url' => 'docs/file-uploads'],
'next' => ['label' => 'Visit onboard', 'url' => 'docs/visit-onboard'],
],
'visit-onboard' => [
'view' => 'docs/visit-onboard',
'title' => 'Visit onboard',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Prerequisites', 'href' => '#prerequisites'],
['label' => 'Entry points', 'href' => '#entry-points'],
['label' => 'Eligibility rules', 'href' => '#eligibility-rules'],
['label' => 'Async flow', 'href' => '#async-flow'],
['label' => 'Family payload', 'href' => '#family-payload'],
['label' => 'API integration', 'href' => '#api-integration'],
['label' => 'Response examples', 'href' => '#response-examples'],
['label' => 'Database updates', 'href' => '#database-updates'],
['label' => 'Failure behavior', 'href' => '#failure-behavior'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#common-pitfalls'],
],
'prev' => ['label' => 'Background Jobs', 'url' => 'docs/background-jobs'],
'next' => ['label' => 'Visit offboard', 'url' => 'docs/visit-offboard'],
],
'visit-offboard' => [
'view' => 'docs/visit-offboard',
'title' => 'Visit offboard',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '6 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'When it is queued', 'href' => '#when-it-is-queued'],
['label' => 'visitOffBoard()', 'href' => '#visit-off-board-api'],
['label' => 'updateVisitoffboardStatus()', 'href' => '#update-status'],
['label' => 'Manual test route', 'href' => '#manual-test-route'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#common-pitfalls'],
],
'prev' => ['label' => 'Visit onboard', 'url' => 'docs/visit-onboard'],
'next' => ['label' => 'Deployment', 'url' => 'docs/deployment'],
],
'deployment' => [
'view' => 'docs/deployment',
'title' => 'Deployment',
'breadcrumb' => 'DevOps',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Dev cPanel flow', 'href' => '#dev-cpanel-flow', 'tag' => 'DEV'],
['label' => 'cPanel deploy scripts', 'href' => '#cpanel-deploy-scripts', 'tag' => 'DEV'],
['label' => 'Branch promotion flow', 'href' => '#branch-promotion-flow', 'tag' => 'UAT/LIVE'],
['label' => 'Auto merge script', 'href' => '#auto-merge-script', 'tag' => 'UAT/LIVE'],
['label' => 'Script usage', 'href' => '#script-usage', 'tag' => 'UAT/LIVE'],
['label' => 'Log output', 'href' => '#log-output', 'tag' => 'UAT/LIVE'],
['label' => 'Operational notes', 'href' => '#operational-notes', 'tag' => 'UAT/LIVE'],
['label' => 'Code move scripts', 'href' => '#code-move-scripts', 'tag' => 'UAT/LIVE'],
['label' => 'Manual fallback', 'href' => '#manual-fallback', 'tag' => 'DEV'],
],
'prev' => ['label' => 'Visit offboard', 'url' => 'docs/visit-offboard'],
'next' => ['label' => 'CI/CD Pipeline', 'url' => 'docs/cicd'],
],
'cicd' => [
'view' => 'docs/cicd',
'title' => 'CI/CD Pipeline',
'breadcrumb' => 'DevOps',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '4 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Dev pipeline', 'href' => '#dev-pipeline', 'tag' => 'DEV'],
['label' => 'UAT and live flow', 'href' => '#uat-live-flow', 'tag' => 'UAT/LIVE'],
['label' => 'Related docs', 'href' => '#related-docs'],
],
'prev' => ['label' => 'Deployment', 'url' => 'docs/deployment'],
'next' => ['label' => 'S3 & CloudFront', 'url' => 'docs/s3-cloudfront'],
],
's3-cloudfront' => [
'view' => 'docs/s3-cloudfront',
'title' => 'S3 & CloudFront',
'breadcrumb' => 'DevOps',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '7 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Prerequisites', 'href' => '#prerequisites'],
['label' => 'Selection flow', 'href' => '#selection-flow'],
['label' => 'Deployment steps', 'href' => '#deployment-steps'],
['label' => 'Invalidation polling', 'href' => '#invalidation-polling'],
['label' => 'Operational notes', 'href' => '#operational-notes'],
['label' => 'Full script', 'href' => '#full-script'],
],
'prev' => ['label' => 'CI/CD Pipeline', 'url' => 'docs/cicd'],
'next' => ['label' => 'Changelog', 'url' => 'docs/changelog'],
],
'api-rate-limiter' => [
'view' => 'docs/api-rate-limiter',
'title' => 'API Rate Limiter',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '10 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Core service', 'href' => '#core-service'],
['label' => 'Auth API filter', 'href' => '#auth-api-filter'],
['label' => 'JWT API filter', 'href' => '#jwt-api-filter'],
['label' => 'Configuration', 'href' => '#configuration'],
['label' => 'Change block counts and durations', 'href' => '#change-block-count-duration'],
['label' => 'Manual unblock samples', 'href' => '#manual-unblock-samples'],
['label' => 'Cache TTL and auto-release', 'href' => '#cache-ttl-auto-release'],
['label' => 'DB reconciliation (cron)', 'href' => '#db-reconciliation-cron'],
['label' => 'HTTP responses', 'href' => '#http-responses'],
['label' => 'Wiring routes', 'href' => '#wiring-routes'],
['label' => 'Blocked list URL', 'href' => '#blocked-list-url'],
['label' => 'Smoke test command', 'href' => '#smoke-test-command'],
['label' => 'Operational notes', 'href' => '#operational-notes'],
],
'prev' => ['label' => 'Email / Notifications', 'url' => 'docs/notifications'],
'next' => ['label' => 'TPA Recon', 'url' => 'docs/tpa-recon'],
],
'tpa-recon' => [
'view' => 'docs/tpa-recon',
'title' => 'TPA Recon',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '18 min read',
'toc' => [
['label' => 'What this is', 'href' => '#what-this-is'],
['label' => 'Glossary', 'href' => '#glossary'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'End-to-end flow', 'href' => '#end-to-end-flow'],
['label' => 'Variation report', 'href' => '#variation-report'],
['label' => 'Classifying rec_type', 'href' => '#classifying-rec-type'],
['label' => 'Linking ref column', 'href' => '#linking-ref-column'],
['label' => 'Proceed next step', 'href' => '#proceed-next-step'],
['label' => 'Not in Nhance → inception', 'href' => '#not-in-nhance-inception'],
['label' => 'Need to review → DB sync', 'href' => '#need-to-review-sync'],
['label' => 'Deletion initialization', 'href' => '#deletion-initialization'],
['label' => 'Background jobs chain', 'href' => '#background-jobs-chain'],
['label' => 'New developer checklist', 'href' => '#new-developer-checklist'],
],
'prev' => ['label' => 'API Rate Limiter', 'url' => 'docs/api-rate-limiter'],
'next' => ['label' => 'EB rack rate config', 'url' => 'docs/eb-rack-rate-config'],
],
'eb-rack-rate-config' => [
'view' => 'docs/eb-rack-rate-config',
'title' => 'EB rack rate config',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '20 min read',
'toc' => [
['label' => 'Purpose', 'href' => '#purpose'],
['label' => 'Premium calculation (all)', 'href' => '#premium-calculation-modes'],
['label' => 'Applicable family members', 'href' => '#applicable-family-members'],
['label' => 'Member radios (Yes / No / …)', 'href' => '#applicable-members-radio-meanings'],
['label' => 'Rack matching vs family', 'href' => '#applicable-members-matching'],
['label' => 'Rack vs family flowchart', 'href' => '#rack-rate-family-matching-flowchart'],
['label' => 'How to configure a rack rate', 'href' => '#how-to-configure-rack-rate'],
['label' => 'Where it appears in the UI', 'href' => '#ui-entry'],
['label' => 'Load grid data (backend)', 'href' => '#getpolicy-grid-data'],
['label' => 'Save rack rate (backend)', 'href' => '#create-premium'],
['label' => 'Frontend flow', 'href' => '#frontend-flow'],
['label' => 'Grid IDs and storage', 'href' => '#grid-ids-storage'],
['label' => 'Grid types (113)', 'href' => '#grid-types-1-13'],
['label' => 'Excel paste path', 'href' => '#excel-paste'],
['label' => 'Related routes', 'href' => '#related-routes'],
],
'prev' => ['label' => 'TPA Recon', 'url' => 'docs/tpa-recon'],
'next' => ['label' => 'EB rack rate calculation', 'url' => 'docs/eb-rack-rate-calculation'],
],
'eb-rack-rate-calculation' => [
'view' => 'docs/eb-rack-rate-calculation',
'title' => 'EB rack rate calculation',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '18 min read',
'toc' => [
['label' => 'Scope and entry point', 'href' => '#scope'],
['label' => 'Data loaded before premium', 'href' => '#inputs'],
['label' => 'Excel row shape (numeric columns)', 'href' => '#excel-columns'],
['label' => 'Flow: employeesOnboardPreprocess', 'href' => '#flow-preprocess'],
['label' => 'Flow: calculate_premium_new', 'href' => '#flow-calculate-premium'],
['label' => 'group_slab_rates_basedon_name', 'href' => '#flow-group-slabs'],
['label' => 'get_familiy_composition', 'href' => '#flow-family-composition'],
['label' => 'compare_incoming…_slab', 'href' => '#flow-compare-slab'],
['label' => 'get_applicable_familiy_members', 'href' => '#flow-applicable-members'],
['label' => 'Per-member transform and gate', 'href' => '#flow-per-member'],
['label' => 'premium_calculation_manager', 'href' => '#flow-premium-manager'],
['label' => 'Dependent addition extras', 'href' => '#dependent-addition'],
['label' => 'Failure: zero successful families', 'href' => '#failure-modes'],
['label' => 'Related', 'href' => '#related'],
],
'prev' => ['label' => 'EB rack rate config', 'url' => 'docs/eb-rack-rate-config'],
'next' => ['label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'],
],
'bds-insurer-statement' => [
'view' => 'docs/bds-insurer-statement',
'title' => 'BDS Insurer statement',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '12 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Row validation logic', 'href' => '#row-validation-flowchart'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'Statement list UI', 'href' => '#statement-list-ui'],
['label' => 'uploadInsurerStatement','href' => '#upload-flow'],
['label' => 'validateInsurerStatement', 'href' => '#validate-flow'],
['label' => 'Validation steps', 'href' => '#validate-steps', 'level' => 'h3'],
['label' => 'Validation error codes', 'href' => '#validation-errors', 'level' => 'h3'],
['label' => 'updateInsurerStatement', 'href' => '#update-flow'],
['label' => 'Excel column mapping', 'href' => '#excel-columns'],
['label' => 'NHance source query', 'href' => '#nhance-source-query'],
['label' => 'Invoice and delete', 'href' => '#invoice-and-delete'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#common-pitfalls'],
['label' => 'Related BDS features', 'href' => '#related-bds'],
],
'prev' => ['label' => 'EB rack rate calculation', 'url' => 'docs/eb-rack-rate-calculation'],
'next' => ['label' => 'BDS commission', 'url' => 'docs/bds-commission'],
],
'bds-commission' => [
'view' => 'docs/bds-commission',
'title' => 'BDS commission',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '10 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Key files', 'href' => '#key-files'],
['label' => 'Routes', 'href' => '#routes'],
['label' => 'Commission file format', 'href' => '#commission-file-format'],
['label' => 'Required columns', 'href' => '#required-columns', 'level' => 'h3'],
['label' => 'Sample rows', 'href' => '#commission-file-samples', 'level' => 'h3'],
['label' => 'Rule upload', 'href' => '#upload-flow'],
['label' => 'Rules editor', 'href' => '#rules-editor'],
['label' => 'Rule JSON shape', 'href' => '#rule-json'],
['label' => 'Calculation API', 'href' => '#calculation-api'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'BDS Insurer statement', 'url' => 'docs/bds-insurer-statement'],
'next' => ['label' => 'Inception', 'url' => 'docs/inception'],
],
'inception' => [
'view' => 'docs/inception',
'title' => 'Inception',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '8 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'],
['label' => 'Format validation', 'href' => '#format-validation'],
['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'],
['label' => 'reason JSON', 'href' => '#reason-json', 'level' => 'h3'],
['label' => 'Data validation', 'href' => '#data-validation'],
['label' => 'Data error codes', 'href' => '#data-errors', 'level' => 'h3'],
['label' => 'Onboard preprocess', 'href' => '#preprocess'],
['label' => 'Excel columns', 'href' => '#excel-columns'],
['label' => 'Family row example', 'href' => '#family-example'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'BDS commission', 'url' => 'docs/bds-commission'],
'next' => ['label' => 'Deletion', 'url' => 'docs/deletion'],
],
'deletion' => [
'view' => 'docs/deletion',
'title' => 'Deletion',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '7 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'],
['label' => 'Format validation', 'href' => '#format-validation'],
['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'],
['label' => 'Data validation', 'href' => '#data-validation'],
['label' => 'employeeDisembark', 'href' => '#disembark'],
['label' => 'TPA auto-deletion', 'href' => '#tpa-auto'],
['label' => 'Excel columns', 'href' => '#excel-columns'],
['label' => 'Row examples', 'href' => '#row-example'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'Inception', 'url' => 'docs/inception'],
'next' => ['label' => 'Correction', 'url' => 'docs/correction'],
],
'correction' => [
'view' => 'docs/correction',
'title' => 'Correction',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '7 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'],
['label' => 'Format validation', 'href' => '#format-validation'],
['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'],
['label' => 'Data validation', 'href' => '#data-validation'],
['label' => 'employeesCorrectionProcess', 'href' => '#correction-process'],
['label' => 'Excel columns', 'href' => '#excel-columns'],
['label' => 'Row examples', 'href' => '#row-example'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'Deletion', 'url' => 'docs/deletion'],
'next' => ['label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'],
],
'si-enhancement' => [
'view' => 'docs/si-enhancement',
'title' => 'SI Enhancement',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '7 min read',
'toc' => [
['label' => 'Overview', 'href' => '#overview'],
['label' => 'Key files and routes', 'href' => '#key-files-routes'],
['label' => 'Sync vs jobs', 'href' => '#sync-vs-jobs'],
['label' => 'Format validation', 'href' => '#format-validation'],
['label' => 'Format error codes', 'href' => '#format-errors', 'level' => 'h3'],
['label' => 'Data validation', 'href' => '#data-validation'],
['label' => 'employeesSIEnhanceProcess', 'href' => '#si-process'],
['label' => 'Excel columns', 'href' => '#excel-columns'],
['label' => 'Row examples', 'href' => '#row-example'],
['label' => 'Developer steps', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'Correction', 'url' => 'docs/correction'],
'next' => ['label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'],
],
'non-eb-opportunities' => [
'view' => 'docs/non-eb-opportunities',
'title' => 'Non-EB Opportunities',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '18 min read',
'toc' => [
['label' => 'End-to-end flow', 'href' => '#overview'],
['label' => 'Key files', 'href' => '#key-files'],
['label' => 'Google Sheet config', 'href' => '#google-sheet-config'],
['label' => 'App ↔ Drive flow', 'href' => '#gsheet-app-drive-flow', 'level' => 'h3'],
['label' => 'Before RFQ / QCR', 'href' => '#gsheet-prerequisites', 'level' => 'h3'],
['label' => 'Sheet ID per product', 'href' => '#gsheet-product-config', 'level' => 'h3'],
['label' => 'App-level config', 'href' => '#gsheet-app-config', 'level' => 'h3'],
['label' => 'Create opportunity', 'href' => '#create-edit'],
['label' => 'Edit flow', 'href' => '#edit-flow'],
['label' => 'List table actions', 'href' => '#list-actions'],
['label' => 'Action endpoint map', 'href' => '#action-map', 'level' => 'h3'],
['label' => 'RFQ', 'href' => '#step-rfq'],
['label' => 'QCR', 'href' => '#step-qcr'],
['label' => 'Mail actions', 'href' => '#step-mails'],
['label' => 'Placement', 'href' => '#step-placement'],
['label' => 'Status lifecycle', 'href' => '#status-lifecycle'],
['label' => 'Controller reference', 'href' => '#controller-reference'],
['label' => 'Developer checklist', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'SI Enhancement', 'url' => 'docs/si-enhancement'],
'next' => ['label' => 'Non-EB Claims', 'url' => 'docs/non-eb-claims'],
],
'non-eb-claims' => [
'view' => 'docs/non-eb-claims',
'title' => 'Non-EB Claims',
'breadcrumb' => 'Features',
'last_updated' => 'May 2026',
'author' => 'Core Team',
'read_time' => '16 min read',
'toc' => [
['label' => 'End-to-end flow', 'href' => '#overview'],
['label' => 'Why policy type 50', 'href' => '#policy-type-50-default'],
['label' => 'Design assumption', 'href' => '#policy-type-50-assumption', 'level' => 'h3'],
['label' => '50 vs selected policy', 'href' => '#policy-type-50-vs-selected-policy', 'level' => 'h3'],
['label' => 'Where 50 is hardcoded', 'href' => '#policy-type-50-where-hardcoded', 'level' => 'h3'],
['label' => 'Different status set', 'href' => '#policy-type-50-future', 'level' => 'h3'],
['label' => 'Key files and routes', 'href' => '#key-files'],
['label' => 'Route map', 'href' => '#route-map', 'level' => 'h3'],
['label' => 'Access control', 'href' => '#access'],
['label' => 'List and filter', 'href' => '#list-flow'],
['label' => 'Create and edit', 'href' => '#create-edit'],
['label' => 'Status-driven sections', 'href' => '#status-sections'],
['label' => 'Auto-mail', 'href' => '#auto-mail'],
['label' => 'Mail template CRUD', 'href' => '#mail-template-crud'],
['label' => 'Template fields', 'href' => '#template-fields', 'level' => 'h3'],
['label' => 'Template actions', 'href' => '#template-actions', 'level' => 'h3'],
['label' => 'Notes', 'href' => '#notes'],
['label' => 'Documents', 'href' => '#documents'],
['label' => 'Reports', 'href' => '#reports'],
['label' => 'Data model', 'href' => '#data-model'],
['label' => 'Controller reference', 'href' => '#controller-reference'],
['label' => 'Developer checklist', 'href' => '#developer-steps'],
['label' => 'Common pitfalls', 'href' => '#pitfalls'],
],
'prev' => ['label' => 'Non-EB Opportunities', 'url' => 'docs/non-eb-opportunities'],
'next' => ['label' => 'Endpoints', 'url' => 'docs/endpoints'],
],
// ── Add more pages here following the same pattern ──────────────────
];
// =========================================================================
// PUBLIC ROUTES
// =========================================================================
/**
* GET /docs → redirect to first page (introduction)
*/
public function index(): \CodeIgniter\HTTP\RedirectResponse
{
return redirect()->to(base_url('docs/introduction'));
}
/**
* GET /docs/(:segment)
*
* Looks up the slug in $pages, assembles layout partials, and returns HTML.
* The individual view only contains its own content — no layout boilerplate.
*/
public function page(string $slug): string
{
// 1. Resolve page config
$config = $this->getPageConfig($slug);
// 2. Render the inner content view (must exist)
$content = $this->renderContentView($config['view'], $config);
// 3. Assemble full layout and return
return $this->renderDocPage($config, $content);
}
// =========================================================================
// CORE LAYOUT ASSEMBLER
// =========================================================================
/**
* Wraps any content string with the full docs layout (header, sidebar,
* main open/close, footer) and returns the complete HTML page.
*
* @param array $config Page config entry from $this->pages
* @param string $content Rendered HTML from the content-only view
*/
protected function renderDocPage(array $config, string $content): string
{
$sharedLayout = [
'app_name' => $this->appName,
'app_version' => $this->appVersion,
'nav' => $this->nav, // sidebar needs the full nav
];
$header = view('docs/partials/docs_header', array_merge($sharedLayout, [
'doc_title' => $config['title'],
]));
$sidebar = view('docs/partials/docs_sidebar', array_merge($sharedLayout, [
'active_page' => $config['id'],
]));
$mainOpen = view('docs/partials/docs_main_open', [
'doc_title' => $config['title'],
'breadcrumb' => $config['breadcrumb'] ?? '',
'last_updated' => $config['last_updated'] ?? '',
'author' => $config['author'] ?? '',
'read_time' => $config['read_time'] ?? '',
'toc' => $config['toc'] ?? [],
]);
$mainClose = view('docs/partials/docs_main_close', [
'toc' => $config['toc'] ?? [],
'prev_label' => $config['prev']['label'] ?? '',
'prev_url' => $config['prev']['url'] ?? '',
'next_label' => $config['next']['label'] ?? '',
'next_url' => $config['next']['url'] ?? '',
]);
$footer = view('docs/partials/docs_footer', [
'app_name' => $this->appName,
]);
return $header . $sidebar . $mainOpen . $content . $mainClose . $footer;
}
// =========================================================================
// HELPERS
// =========================================================================
/**
* Resolve a URL slug to its page config array.
* Injects 'id' (the slug) so partials can use it without repeating it.
*
* @throws PageNotFoundException
*/
protected function getPageConfig(string $slug): array
{
if (! array_key_exists($slug, $this->pages)) {
throw new PageNotFoundException("Docs page not found: {$slug}");
}
return array_merge($this->pages[$slug], ['id' => $slug]);
}
/**
* Render the content-only view file.
* Passes the full config as view data so the content view
* can access $title, $toc, etc. if needed.
*
* @throws PageNotFoundException if the view file does not exist on disk
*/
protected function renderContentView(string $viewPath, array $data = []): string
{
$fullPath = APPPATH . 'Views/' . $viewPath . '.php';
if (! file_exists($fullPath)) {
throw new PageNotFoundException("Docs view file not found: {$viewPath}.php");
}
// Remove 'view' key so it doesn't shadow anything inside the view
unset($data['view']);
return view($viewPath, $data);
}
}