812 lines
46 KiB
PHP
812 lines
46 KiB
PHP
<?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 don’t', '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 (1–13)', '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);
|
||
}
|
||
}
|