secubox-openwrt/site/code-templates/index.html
CyberMind-FR ce543762cc chore: Update GitHub repo URL to CyberMind-FR organization
Replace github.com/gkerma/secubox-openwrt with
github.com/CyberMind-FR/secubox-openwrt across all files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 09:44:01 +01:00

3374 lines
308 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="OpenWrt LuCI Security & Management Suite">
<meta name="author" content="CyberMind.fr">
<link rel="canonical" href="https://gkerma.github.io/secubox-openwrt/code-templates/">
<link rel="prev" href="../development-guidelines/">
<link rel="next" href="../module-implementation-guide/">
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>Code Templates - SecuBox Documentation</title>
<link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../stylesheets/extra.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="purple">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#secubox-module-code-templates" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<div data-md-color-scheme="default" data-md-component="outdated" hidden>
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="SecuBox Documentation" class="md-header__button md-logo" aria-label="SecuBox Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
SecuBox Documentation
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Code Templates
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="purple" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="purple" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/CyberMind-FR/secubox-openwrt" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
gkerma/secubox-openwrt
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href=".." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../quick-start/" class="md-tabs__link">
Getting Started
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../development-guidelines/" class="md-tabs__link">
Development
</a>
</li>
<li class="md-tabs__item">
<a href="../claude/" class="md-tabs__link">
Reference
</a>
</li>
<li class="md-tabs__item">
<a href="../module-status/" class="md-tabs__link">
Modules
</a>
</li>
<li class="md-tabs__item">
<a href="../todo-analyse/" class="md-tabs__link">
Tools & Roadmap
</a>
</li>
<li class="md-tabs__item">
<a href="../archive/" class="md-tabs__link">
Archive
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="SecuBox Documentation" class="md-nav__button md-logo" aria-label="SecuBox Documentation" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
SecuBox Documentation
</label>
<div class="md-nav__source">
<a href="https://github.com/CyberMind-FR/secubox-openwrt" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</div>
<div class="md-source__repository">
gkerma/secubox-openwrt
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href=".." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
<span class="md-ellipsis">
Getting Started
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../quick-start/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../documentation-index/" class="md-nav__link">
<span class="md-ellipsis">
Documentation Index
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../repository-guidelines/" class="md-nav__link">
<span class="md-ellipsis">
Repository Guidelines
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" checked>
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="">
<span class="md-ellipsis">
Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../development-guidelines/" class="md-nav__link">
<span class="md-ellipsis">
Development Guidelines
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Code Templates
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Code Templates
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#see-also" class="md-nav__link">
<span class="md-ellipsis">
See Also
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#table-of-contents" class="md-nav__link">
<span class="md-ellipsis">
Table of Contents
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#file-structure-template" class="md-nav__link">
<span class="md-ellipsis">
File Structure Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-module-template" class="md-nav__link">
<span class="md-ellipsis">
API Module Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#javascript-view-template" class="md-nav__link">
<span class="md-ellipsis">
JavaScript View Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#rpcd-backend-template" class="md-nav__link">
<span class="md-ellipsis">
RPCD Backend Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#menu-json-template" class="md-nav__link">
<span class="md-ellipsis">
Menu JSON Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#acl-json-template" class="md-nav__link">
<span class="md-ellipsis">
ACL JSON Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#css-styling-template" class="md-nav__link">
<span class="md-ellipsis">
CSS Styling Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#complete-implementation-example" class="md-nav__link">
<span class="md-ellipsis">
Complete Implementation Example
</span>
</a>
<nav class="md-nav" aria-label="Complete Implementation Example">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#directory-structure" class="md-nav__link">
<span class="md-ellipsis">
Directory Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#apijs" class="md-nav__link">
<span class="md-ellipsis">
api.js
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#overviewjs" class="md-nav__link">
<span class="md-ellipsis">
overview.js
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#luciexample-dashboard-rpcd" class="md-nav__link">
<span class="md-ellipsis">
luci.example-dashboard (RPCD)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#menudluci-app-example-dashboardjson" class="md-nav__link">
<span class="md-ellipsis">
menu.d/luci-app-example-dashboard.json
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#acldluci-app-example-dashboardjson" class="md-nav__link">
<span class="md-ellipsis">
acl.d/luci-app-example-dashboard.json
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#dashboardcss" class="md-nav__link">
<span class="md-ellipsis">
dashboard.css
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-pitfalls-and-solutions" class="md-nav__link">
<span class="md-ellipsis">
Common Pitfalls and Solutions
</span>
</a>
<nav class="md-nav" aria-label="Common Pitfalls and Solutions">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-rpcd-object-not-found-error" class="md-nav__link">
<span class="md-ellipsis">
1. RPCD "Object not found" Error
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-http-404-view-not-found" class="md-nav__link">
<span class="md-ellipsis">
2. HTTP 404 View Not Found
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-acl-permission-denied" class="md-nav__link">
<span class="md-ellipsis">
3. ACL Permission Denied
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-css-not-loading" class="md-nav__link">
<span class="md-ellipsis">
4. CSS Not Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-auto-refresh-not-working" class="md-nav__link">
<span class="md-ellipsis">
5. Auto-Refresh Not Working
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#validation-checklist" class="md-nav__link">
<span class="md-ellipsis">
Validation Checklist
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../module-implementation-guide/" class="md-nav__link">
<span class="md-ellipsis">
Module Implementation
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Reference
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../claude/" class="md-nav__link">
<span class="md-ellipsis">
RPCD & Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../validation-guide/" class="md-nav__link">
<span class="md-ellipsis">
Validation Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../permissions-guide/" class="md-nav__link">
<span class="md-ellipsis">
Permissions Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../luci-development-reference/" class="md-nav__link">
<span class="md-ellipsis">
LuCI Development
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../codex/" class="md-nav__link">
<span class="md-ellipsis">
Codex Manual
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Modules
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../module-status/" class="md-nav__link">
<span class="md-ellipsis">
Module Status
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../feature-regeneration-prompts/" class="md-nav__link">
<span class="md-ellipsis">
Feature Prompts
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_6" >
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
<span class="md-ellipsis">
Tools & Roadmap
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Tools & Roadmap
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../todo-analyse/" class="md-nav__link">
<span class="md-ellipsis">
TODO Roadmap
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_7" >
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0">
<span class="md-ellipsis">
Archive
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Archive
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../archive/" class="md-nav__link">
<span class="md-ellipsis">
Archive Index
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../archive/build-issues/" class="md-nav__link">
<span class="md-ellipsis">
Build Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../archive/completion-report/" class="md-nav__link">
<span class="md-ellipsis">
Completion Report
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../archive/module-enable-disable-design/" class="md-nav__link">
<span class="md-ellipsis">
Module Enable/Disable
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#see-also" class="md-nav__link">
<span class="md-ellipsis">
See Also
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#table-of-contents" class="md-nav__link">
<span class="md-ellipsis">
Table of Contents
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#file-structure-template" class="md-nav__link">
<span class="md-ellipsis">
File Structure Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-module-template" class="md-nav__link">
<span class="md-ellipsis">
API Module Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#javascript-view-template" class="md-nav__link">
<span class="md-ellipsis">
JavaScript View Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#rpcd-backend-template" class="md-nav__link">
<span class="md-ellipsis">
RPCD Backend Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#menu-json-template" class="md-nav__link">
<span class="md-ellipsis">
Menu JSON Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#acl-json-template" class="md-nav__link">
<span class="md-ellipsis">
ACL JSON Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#css-styling-template" class="md-nav__link">
<span class="md-ellipsis">
CSS Styling Template
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#complete-implementation-example" class="md-nav__link">
<span class="md-ellipsis">
Complete Implementation Example
</span>
</a>
<nav class="md-nav" aria-label="Complete Implementation Example">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#directory-structure" class="md-nav__link">
<span class="md-ellipsis">
Directory Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#apijs" class="md-nav__link">
<span class="md-ellipsis">
api.js
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#overviewjs" class="md-nav__link">
<span class="md-ellipsis">
overview.js
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#luciexample-dashboard-rpcd" class="md-nav__link">
<span class="md-ellipsis">
luci.example-dashboard (RPCD)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#menudluci-app-example-dashboardjson" class="md-nav__link">
<span class="md-ellipsis">
menu.d/luci-app-example-dashboard.json
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#acldluci-app-example-dashboardjson" class="md-nav__link">
<span class="md-ellipsis">
acl.d/luci-app-example-dashboard.json
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#dashboardcss" class="md-nav__link">
<span class="md-ellipsis">
dashboard.css
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#common-pitfalls-and-solutions" class="md-nav__link">
<span class="md-ellipsis">
Common Pitfalls and Solutions
</span>
</a>
<nav class="md-nav" aria-label="Common Pitfalls and Solutions">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-rpcd-object-not-found-error" class="md-nav__link">
<span class="md-ellipsis">
1. RPCD "Object not found" Error
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-http-404-view-not-found" class="md-nav__link">
<span class="md-ellipsis">
2. HTTP 404 View Not Found
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-acl-permission-denied" class="md-nav__link">
<span class="md-ellipsis">
3. ACL Permission Denied
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-css-not-loading" class="md-nav__link">
<span class="md-ellipsis">
4. CSS Not Loading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-auto-refresh-not-working" class="md-nav__link">
<span class="md-ellipsis">
5. Auto-Refresh Not Working
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#validation-checklist" class="md-nav__link">
<span class="md-ellipsis">
Validation Checklist
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="secubox-module-code-templates">SecuBox Module Code Templates<a class="headerlink" href="#secubox-module-code-templates" title="Permanent link">&para;</a></h1>
<p><strong>Version:</strong> 1.0.0<br />
<strong>Last Updated:</strong> 2025-12-28<br />
<strong>Status:</strong> Active<br />
<strong>Purpose:</strong> Ready-to-use code templates extracted from working SecuBox modules</p>
<hr />
<h2 id="see-also">See Also<a class="headerlink" href="#see-also" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>Implementation Workflow:</strong> <a href="../module-implementation-guide/">MODULE-IMPLEMENTATION-GUIDE.md</a></li>
<li><strong>Quick Commands:</strong> <a href="../quick-start/">QUICK-START.md</a></li>
<li><strong>Automation Guardrails:</strong> <a href="../codex/">CODEX.md</a></li>
<li><strong>Module Prompts:</strong> <a href="../feature-regeneration-prompts/">FEATURE-REGENERATION-PROMPTS.md</a></li>
</ul>
<hr />
<h2 id="table-of-contents">Table of Contents<a class="headerlink" href="#table-of-contents" title="Permanent link">&para;</a></h2>
<ol>
<li><a href="#file-structure-template">File Structure Template</a></li>
<li><a href="#api-module-template">API Module Template</a></li>
<li><a href="#javascript-view-template">JavaScript View Template</a></li>
<li><a href="#rpcd-backend-template">RPCD Backend Template</a></li>
<li><a href="#menu-json-template">Menu JSON Template</a></li>
<li><a href="#acl-json-template">ACL JSON Template</a></li>
<li><a href="#css-styling-template">CSS Styling Template</a></li>
<li><a href="#complete-implementation-example">Complete Implementation Example</a></li>
</ol>
<hr />
<h2 id="file-structure-template">File Structure Template<a class="headerlink" href="#file-structure-template" title="Permanent link">&para;</a></h2>
<p>Every SecuBox module follows this exact structure:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>luci-app-&lt;module-name&gt;/
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>├── Makefile # OpenWrt package definition
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>├── README.md # Module documentation
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>├── htdocs/luci-static/resources/
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>│ ├── &lt;module-name&gt;/
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>│ │ ├── api.js # RPC API client (REQUIRED)
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>│ │ ├── theme.js # Theme helper (optional)
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a>│ │ └── dashboard.css # Module-specific styles
<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>│ └── view/&lt;module-name&gt;/
<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>│ ├── overview.js # Main dashboard view
<a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a>│ ├── settings.js # Settings view (if needed)
<a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a>│ └── *.js # Additional views
<a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a>└── root/
<a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> ├── etc/config/&lt;module-name&gt; # UCI config (optional)
<a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> └── usr/
<a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> ├── libexec/rpcd/
<a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> │ └── luci.&lt;module-name&gt; # RPCD backend (REQUIRED, must be executable)
<a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> └── share/
<a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> ├── luci/menu.d/
<a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> │ └── luci-app-&lt;module-name&gt;.json # Menu definition
<a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> └── rpcd/acl.d/
<a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> └── luci-app-&lt;module-name&gt;.json # ACL permissions
</code></pre></div>
<p><strong>Critical Rules:</strong>
1. RPCD script MUST be named <code>luci.&lt;module-name&gt;</code> (with <code>luci.</code> prefix)
2. RPCD script MUST be executable (<code>chmod +x</code>)
3. Menu paths MUST match view file locations
4. CSS/JS files should be 644 permissions
5. All ubus objects MUST use <code>luci.</code> prefix</p>
<hr />
<h2 id="api-module-template">API Module Template<a class="headerlink" href="#api-module-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>htdocs/luci-static/resources/&lt;module-name&gt;/api.js</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="s1">&#39;use strict&#39;</span><span class="p">;</span>
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="s1">&#39;require baseclass&#39;</span><span class="p">;</span>
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="s1">&#39;require rpc&#39;</span><span class="p">;</span>
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="cm">/**</span>
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a><span class="cm"> * [Module Name] API</span>
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="cm"> * Package: luci-app-&lt;module-name&gt;</span>
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a><span class="cm"> * RPCD object: luci.&lt;module-name&gt;</span>
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="cm"> * Version: 1.0.0</span>
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="cm"> */</span>
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a>
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="c1">// Debug log to verify correct version is loaded</span>
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;🔧 [Module Name] API v1.0.0 loaded at&#39;</span><span class="p">,</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">());</span>
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a>
<a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a><span class="c1">// RPC Method Declarations</span>
<a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a>
<a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a><span class="c1">// Simple method (no parameters)</span>
<a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a><span class="kd">var</span><span class="w"> </span><span class="nx">callStatus</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">rpc</span><span class="p">.</span><span class="nx">declare</span><span class="p">({</span>
<a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a><span class="w"> </span><span class="nx">object</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;luci.&lt;module-name&gt;&#39;</span><span class="p">,</span><span class="w"> </span><span class="c1">// MUST match RPCD script name</span>
<a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a><span class="w"> </span><span class="nx">method</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;status&#39;</span><span class="p">,</span>
<a id="__codelineno-1-23" name="__codelineno-1-23" href="#__codelineno-1-23"></a><span class="w"> </span><span class="nx">expect</span><span class="o">:</span><span class="w"> </span><span class="p">{}</span>
<a id="__codelineno-1-24" name="__codelineno-1-24" href="#__codelineno-1-24"></a><span class="p">});</span>
<a id="__codelineno-1-25" name="__codelineno-1-25" href="#__codelineno-1-25"></a>
<a id="__codelineno-1-26" name="__codelineno-1-26" href="#__codelineno-1-26"></a><span class="c1">// Method with return structure</span>
<a id="__codelineno-1-27" name="__codelineno-1-27" href="#__codelineno-1-27"></a><span class="kd">var</span><span class="w"> </span><span class="nx">callGetData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">rpc</span><span class="p">.</span><span class="nx">declare</span><span class="p">({</span>
<a id="__codelineno-1-28" name="__codelineno-1-28" href="#__codelineno-1-28"></a><span class="w"> </span><span class="nx">object</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;luci.&lt;module-name&gt;&#39;</span><span class="p">,</span>
<a id="__codelineno-1-29" name="__codelineno-1-29" href="#__codelineno-1-29"></a><span class="w"> </span><span class="nx">method</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;get_data&#39;</span><span class="p">,</span>
<a id="__codelineno-1-30" name="__codelineno-1-30" href="#__codelineno-1-30"></a><span class="w"> </span><span class="nx">expect</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="c1">// Expected return structure</span>
<a id="__codelineno-1-31" name="__codelineno-1-31" href="#__codelineno-1-31"></a><span class="p">});</span>
<a id="__codelineno-1-32" name="__codelineno-1-32" href="#__codelineno-1-32"></a>
<a id="__codelineno-1-33" name="__codelineno-1-33" href="#__codelineno-1-33"></a><span class="c1">// Method with parameters</span>
<a id="__codelineno-1-34" name="__codelineno-1-34" href="#__codelineno-1-34"></a><span class="kd">var</span><span class="w"> </span><span class="nx">callPerformAction</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">rpc</span><span class="p">.</span><span class="nx">declare</span><span class="p">({</span>
<a id="__codelineno-1-35" name="__codelineno-1-35" href="#__codelineno-1-35"></a><span class="w"> </span><span class="nx">object</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;luci.&lt;module-name&gt;&#39;</span><span class="p">,</span>
<a id="__codelineno-1-36" name="__codelineno-1-36" href="#__codelineno-1-36"></a><span class="w"> </span><span class="nx">method</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;perform_action&#39;</span><span class="p">,</span>
<a id="__codelineno-1-37" name="__codelineno-1-37" href="#__codelineno-1-37"></a><span class="w"> </span><span class="nx">params</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;action_type&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;target&#39;</span><span class="p">],</span><span class="w"> </span><span class="c1">// Parameter names</span>
<a id="__codelineno-1-38" name="__codelineno-1-38" href="#__codelineno-1-38"></a><span class="w"> </span><span class="nx">expect</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">success</span><span class="o">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-1-39" name="__codelineno-1-39" href="#__codelineno-1-39"></a><span class="p">});</span>
<a id="__codelineno-1-40" name="__codelineno-1-40" href="#__codelineno-1-40"></a>
<a id="__codelineno-1-41" name="__codelineno-1-41" href="#__codelineno-1-41"></a><span class="c1">// Method with multiple parameters</span>
<a id="__codelineno-1-42" name="__codelineno-1-42" href="#__codelineno-1-42"></a><span class="kd">var</span><span class="w"> </span><span class="nx">callUpdateConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">rpc</span><span class="p">.</span><span class="nx">declare</span><span class="p">({</span>
<a id="__codelineno-1-43" name="__codelineno-1-43" href="#__codelineno-1-43"></a><span class="w"> </span><span class="nx">object</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;luci.&lt;module-name&gt;&#39;</span><span class="p">,</span>
<a id="__codelineno-1-44" name="__codelineno-1-44" href="#__codelineno-1-44"></a><span class="w"> </span><span class="nx">method</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;update_config&#39;</span><span class="p">,</span>
<a id="__codelineno-1-45" name="__codelineno-1-45" href="#__codelineno-1-45"></a><span class="w"> </span><span class="nx">params</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;key&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;value&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;persist&#39;</span><span class="p">],</span>
<a id="__codelineno-1-46" name="__codelineno-1-46" href="#__codelineno-1-46"></a><span class="w"> </span><span class="nx">expect</span><span class="o">:</span><span class="w"> </span><span class="p">{}</span>
<a id="__codelineno-1-47" name="__codelineno-1-47" href="#__codelineno-1-47"></a><span class="p">});</span>
<a id="__codelineno-1-48" name="__codelineno-1-48" href="#__codelineno-1-48"></a>
<a id="__codelineno-1-49" name="__codelineno-1-49" href="#__codelineno-1-49"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-50" name="__codelineno-1-50" href="#__codelineno-1-50"></a><span class="c1">// Helper Functions (Optional)</span>
<a id="__codelineno-1-51" name="__codelineno-1-51" href="#__codelineno-1-51"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-52" name="__codelineno-1-52" href="#__codelineno-1-52"></a>
<a id="__codelineno-1-53" name="__codelineno-1-53" href="#__codelineno-1-53"></a><span class="cm">/**</span>
<a id="__codelineno-1-54" name="__codelineno-1-54" href="#__codelineno-1-54"></a><span class="cm"> * Format bytes to human-readable string</span>
<a id="__codelineno-1-55" name="__codelineno-1-55" href="#__codelineno-1-55"></a><span class="cm"> */</span>
<a id="__codelineno-1-56" name="__codelineno-1-56" href="#__codelineno-1-56"></a><span class="kd">function</span><span class="w"> </span><span class="nx">formatBytes</span><span class="p">(</span><span class="nx">bytes</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-57" name="__codelineno-1-57" href="#__codelineno-1-57"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">bytes</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;0 B&#39;</span><span class="p">;</span>
<a id="__codelineno-1-58" name="__codelineno-1-58" href="#__codelineno-1-58"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">1024</span><span class="p">;</span>
<a id="__codelineno-1-59" name="__codelineno-1-59" href="#__codelineno-1-59"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">sizes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;B&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;KB&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;MB&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;GB&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;TB&#39;</span><span class="p">];</span>
<a id="__codelineno-1-60" name="__codelineno-1-60" href="#__codelineno-1-60"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">bytes</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">k</span><span class="p">));</span>
<a id="__codelineno-1-61" name="__codelineno-1-61" href="#__codelineno-1-61"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">parseFloat</span><span class="p">((</span><span class="nx">bytes</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">pow</span><span class="p">(</span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">)).</span><span class="nx">toFixed</span><span class="p">(</span><span class="mf">2</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39; &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">sizes</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<a id="__codelineno-1-62" name="__codelineno-1-62" href="#__codelineno-1-62"></a><span class="p">}</span>
<a id="__codelineno-1-63" name="__codelineno-1-63" href="#__codelineno-1-63"></a>
<a id="__codelineno-1-64" name="__codelineno-1-64" href="#__codelineno-1-64"></a><span class="cm">/**</span>
<a id="__codelineno-1-65" name="__codelineno-1-65" href="#__codelineno-1-65"></a><span class="cm"> * Format timestamp to &quot;X ago&quot; string</span>
<a id="__codelineno-1-66" name="__codelineno-1-66" href="#__codelineno-1-66"></a><span class="cm"> */</span>
<a id="__codelineno-1-67" name="__codelineno-1-67" href="#__codelineno-1-67"></a><span class="kd">function</span><span class="w"> </span><span class="nx">formatTimeAgo</span><span class="p">(</span><span class="nx">timestamp</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-68" name="__codelineno-1-68" href="#__codelineno-1-68"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">timestamp</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;Never&#39;</span><span class="p">;</span>
<a id="__codelineno-1-69" name="__codelineno-1-69" href="#__codelineno-1-69"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">now</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">1000</span><span class="p">);</span>
<a id="__codelineno-1-70" name="__codelineno-1-70" href="#__codelineno-1-70"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">diff</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">now</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">timestamp</span><span class="p">;</span>
<a id="__codelineno-1-71" name="__codelineno-1-71" href="#__codelineno-1-71"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mf">60</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">diff</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;s ago&#39;</span><span class="p">;</span>
<a id="__codelineno-1-72" name="__codelineno-1-72" href="#__codelineno-1-72"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mf">3600</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">60</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;m ago&#39;</span><span class="p">;</span>
<a id="__codelineno-1-73" name="__codelineno-1-73" href="#__codelineno-1-73"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mf">86400</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">3600</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;h ago&#39;</span><span class="p">;</span>
<a id="__codelineno-1-74" name="__codelineno-1-74" href="#__codelineno-1-74"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">diff</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">86400</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;d ago&#39;</span><span class="p">;</span>
<a id="__codelineno-1-75" name="__codelineno-1-75" href="#__codelineno-1-75"></a><span class="p">}</span>
<a id="__codelineno-1-76" name="__codelineno-1-76" href="#__codelineno-1-76"></a>
<a id="__codelineno-1-77" name="__codelineno-1-77" href="#__codelineno-1-77"></a><span class="cm">/**</span>
<a id="__codelineno-1-78" name="__codelineno-1-78" href="#__codelineno-1-78"></a><span class="cm"> * Format uptime seconds to &quot;Xd Xh Xm&quot; string</span>
<a id="__codelineno-1-79" name="__codelineno-1-79" href="#__codelineno-1-79"></a><span class="cm"> */</span>
<a id="__codelineno-1-80" name="__codelineno-1-80" href="#__codelineno-1-80"></a><span class="kd">function</span><span class="w"> </span><span class="nx">formatUptime</span><span class="p">(</span><span class="nx">seconds</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-81" name="__codelineno-1-81" href="#__codelineno-1-81"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">days</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">seconds</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">86400</span><span class="p">);</span>
<a id="__codelineno-1-82" name="__codelineno-1-82" href="#__codelineno-1-82"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">hours</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">((</span><span class="nx">seconds</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">86400</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">3600</span><span class="p">);</span>
<a id="__codelineno-1-83" name="__codelineno-1-83" href="#__codelineno-1-83"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">mins</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">((</span><span class="nx">seconds</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">3600</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">60</span><span class="p">);</span>
<a id="__codelineno-1-84" name="__codelineno-1-84" href="#__codelineno-1-84"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">days</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;d &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">hours</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;h &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">mins</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;m&#39;</span><span class="p">;</span>
<a id="__codelineno-1-85" name="__codelineno-1-85" href="#__codelineno-1-85"></a><span class="p">}</span>
<a id="__codelineno-1-86" name="__codelineno-1-86" href="#__codelineno-1-86"></a>
<a id="__codelineno-1-87" name="__codelineno-1-87" href="#__codelineno-1-87"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-88" name="__codelineno-1-88" href="#__codelineno-1-88"></a><span class="c1">// API Export</span>
<a id="__codelineno-1-89" name="__codelineno-1-89" href="#__codelineno-1-89"></a><span class="c1">// ============================================================================</span>
<a id="__codelineno-1-90" name="__codelineno-1-90" href="#__codelineno-1-90"></a>
<a id="__codelineno-1-91" name="__codelineno-1-91" href="#__codelineno-1-91"></a><span class="k">return</span><span class="w"> </span><span class="nx">baseclass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<a id="__codelineno-1-92" name="__codelineno-1-92" href="#__codelineno-1-92"></a><span class="w"> </span><span class="c1">// RPC methods - exposed via ubus</span>
<a id="__codelineno-1-93" name="__codelineno-1-93" href="#__codelineno-1-93"></a><span class="w"> </span><span class="nx">getStatus</span><span class="o">:</span><span class="w"> </span><span class="nx">callStatus</span><span class="p">,</span>
<a id="__codelineno-1-94" name="__codelineno-1-94" href="#__codelineno-1-94"></a><span class="w"> </span><span class="nx">getData</span><span class="o">:</span><span class="w"> </span><span class="nx">callGetData</span><span class="p">,</span>
<a id="__codelineno-1-95" name="__codelineno-1-95" href="#__codelineno-1-95"></a><span class="w"> </span><span class="nx">performAction</span><span class="o">:</span><span class="w"> </span><span class="nx">callPerformAction</span><span class="p">,</span>
<a id="__codelineno-1-96" name="__codelineno-1-96" href="#__codelineno-1-96"></a><span class="w"> </span><span class="nx">updateConfig</span><span class="o">:</span><span class="w"> </span><span class="nx">callUpdateConfig</span><span class="p">,</span>
<a id="__codelineno-1-97" name="__codelineno-1-97" href="#__codelineno-1-97"></a>
<a id="__codelineno-1-98" name="__codelineno-1-98" href="#__codelineno-1-98"></a><span class="w"> </span><span class="c1">// Helper functions</span>
<a id="__codelineno-1-99" name="__codelineno-1-99" href="#__codelineno-1-99"></a><span class="w"> </span><span class="nx">formatBytes</span><span class="o">:</span><span class="w"> </span><span class="nx">formatBytes</span><span class="p">,</span>
<a id="__codelineno-1-100" name="__codelineno-1-100" href="#__codelineno-1-100"></a><span class="w"> </span><span class="nx">formatTimeAgo</span><span class="o">:</span><span class="w"> </span><span class="nx">formatTimeAgo</span><span class="p">,</span>
<a id="__codelineno-1-101" name="__codelineno-1-101" href="#__codelineno-1-101"></a><span class="w"> </span><span class="nx">formatUptime</span><span class="o">:</span><span class="w"> </span><span class="nx">formatUptime</span><span class="p">,</span>
<a id="__codelineno-1-102" name="__codelineno-1-102" href="#__codelineno-1-102"></a>
<a id="__codelineno-1-103" name="__codelineno-1-103" href="#__codelineno-1-103"></a><span class="w"> </span><span class="c1">// Aggregate function for overview page (combines multiple calls)</span>
<a id="__codelineno-1-104" name="__codelineno-1-104" href="#__codelineno-1-104"></a><span class="w"> </span><span class="nx">getAllData</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-105" name="__codelineno-1-105" href="#__codelineno-1-105"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
<a id="__codelineno-1-106" name="__codelineno-1-106" href="#__codelineno-1-106"></a><span class="w"> </span><span class="nx">callStatus</span><span class="p">(),</span>
<a id="__codelineno-1-107" name="__codelineno-1-107" href="#__codelineno-1-107"></a><span class="w"> </span><span class="nx">callGetData</span><span class="p">()</span>
<a id="__codelineno-1-108" name="__codelineno-1-108" href="#__codelineno-1-108"></a><span class="w"> </span><span class="p">]).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">results</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-109" name="__codelineno-1-109" href="#__codelineno-1-109"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-1-110" name="__codelineno-1-110" href="#__codelineno-1-110"></a><span class="w"> </span><span class="nx">status</span><span class="o">:</span><span class="w"> </span><span class="nx">results</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{},</span>
<a id="__codelineno-1-111" name="__codelineno-1-111" href="#__codelineno-1-111"></a><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="nx">results</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-1-112" name="__codelineno-1-112" href="#__codelineno-1-112"></a><span class="w"> </span><span class="p">};</span>
<a id="__codelineno-1-113" name="__codelineno-1-113" href="#__codelineno-1-113"></a><span class="w"> </span><span class="p">});</span>
<a id="__codelineno-1-114" name="__codelineno-1-114" href="#__codelineno-1-114"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-1-115" name="__codelineno-1-115" href="#__codelineno-1-115"></a><span class="p">});</span>
</code></pre></div>
<p><strong>Key Points:</strong>
- Always use <code>'use strict';</code>
- Require <code>baseclass</code> and <code>rpc</code>
- Use <code>rpc.declare()</code> for each RPCD method
- Export from <code>baseclass.extend()</code>
- Helper functions can be included in the API module
- Aggregate functions are useful for views that need multiple data sources</p>
<hr />
<h2 id="javascript-view-template">JavaScript View Template<a class="headerlink" href="#javascript-view-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>htdocs/luci-static/resources/view/&lt;module-name&gt;/overview.js</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="s1">&#39;use strict&#39;</span><span class="p">;</span>
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="s1">&#39;require view&#39;</span><span class="p">;</span>
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="s1">&#39;require ui&#39;</span><span class="p">;</span>
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="s1">&#39;require dom&#39;</span><span class="p">;</span>
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="s1">&#39;require poll&#39;</span><span class="p">;</span>
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a><span class="s1">&#39;require &lt;module-name&gt;/api as API&#39;</span><span class="p">;</span>
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a>
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a><span class="cm">/**</span>
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="cm"> * [Module Name] - Overview Dashboard</span>
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="cm"> * Main view for luci-app-&lt;module-name&gt;</span>
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="cm"> */</span>
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a>
<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a><span class="k">return</span><span class="w"> </span><span class="nx">view</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a><span class="w"> </span><span class="c1">// Data Properties</span>
<a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a>
<a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a><span class="w"> </span><span class="nx">statusData</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-2-19" name="__codelineno-2-19" href="#__codelineno-2-19"></a><span class="w"> </span><span class="nx">componentData</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-2-20" name="__codelineno-2-20" href="#__codelineno-2-20"></a>
<a id="__codelineno-2-21" name="__codelineno-2-21" href="#__codelineno-2-21"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-22" name="__codelineno-2-22" href="#__codelineno-2-22"></a><span class="w"> </span><span class="c1">// Load Data</span>
<a id="__codelineno-2-23" name="__codelineno-2-23" href="#__codelineno-2-23"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-24" name="__codelineno-2-24" href="#__codelineno-2-24"></a>
<a id="__codelineno-2-25" name="__codelineno-2-25" href="#__codelineno-2-25"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-26" name="__codelineno-2-26" href="#__codelineno-2-26"></a><span class="cm"> * Called when view is loaded</span>
<a id="__codelineno-2-27" name="__codelineno-2-27" href="#__codelineno-2-27"></a><span class="cm"> * Return a Promise (can use Promise.all for parallel loading)</span>
<a id="__codelineno-2-28" name="__codelineno-2-28" href="#__codelineno-2-28"></a><span class="cm"> */</span>
<a id="__codelineno-2-29" name="__codelineno-2-29" href="#__codelineno-2-29"></a><span class="w"> </span><span class="nx">load</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-30" name="__codelineno-2-30" href="#__codelineno-2-30"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
<a id="__codelineno-2-31" name="__codelineno-2-31" href="#__codelineno-2-31"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getStatus</span><span class="p">(),</span>
<a id="__codelineno-2-32" name="__codelineno-2-32" href="#__codelineno-2-32"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getData</span><span class="p">()</span>
<a id="__codelineno-2-33" name="__codelineno-2-33" href="#__codelineno-2-33"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-34" name="__codelineno-2-34" href="#__codelineno-2-34"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-35" name="__codelineno-2-35" href="#__codelineno-2-35"></a>
<a id="__codelineno-2-36" name="__codelineno-2-36" href="#__codelineno-2-36"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-37" name="__codelineno-2-37" href="#__codelineno-2-37"></a><span class="w"> </span><span class="c1">// Render View</span>
<a id="__codelineno-2-38" name="__codelineno-2-38" href="#__codelineno-2-38"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-39" name="__codelineno-2-39" href="#__codelineno-2-39"></a>
<a id="__codelineno-2-40" name="__codelineno-2-40" href="#__codelineno-2-40"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-41" name="__codelineno-2-41" href="#__codelineno-2-41"></a><span class="cm"> * Called after load() completes</span>
<a id="__codelineno-2-42" name="__codelineno-2-42" href="#__codelineno-2-42"></a><span class="cm"> * @param {Array} data - Results from load() Promise.all</span>
<a id="__codelineno-2-43" name="__codelineno-2-43" href="#__codelineno-2-43"></a><span class="cm"> */</span>
<a id="__codelineno-2-44" name="__codelineno-2-44" href="#__codelineno-2-44"></a><span class="w"> </span><span class="nx">render</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-45" name="__codelineno-2-45" href="#__codelineno-2-45"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">;</span>
<a id="__codelineno-2-46" name="__codelineno-2-46" href="#__codelineno-2-46"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{};</span>
<a id="__codelineno-2-47" name="__codelineno-2-47" href="#__codelineno-2-47"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">componentData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">};</span>
<a id="__codelineno-2-48" name="__codelineno-2-48" href="#__codelineno-2-48"></a>
<a id="__codelineno-2-49" name="__codelineno-2-49" href="#__codelineno-2-49"></a><span class="w"> </span><span class="c1">// Main container</span>
<a id="__codelineno-2-50" name="__codelineno-2-50" href="#__codelineno-2-50"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">container</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;module-dashboard&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-51" name="__codelineno-2-51" href="#__codelineno-2-51"></a><span class="w"> </span><span class="c1">// Load CSS</span>
<a id="__codelineno-2-52" name="__codelineno-2-52" href="#__codelineno-2-52"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;link&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;rel&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;stylesheet&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;href&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">resource</span><span class="p">(</span><span class="s1">&#39;&lt;module-name&gt;/dashboard.css&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">}),</span>
<a id="__codelineno-2-53" name="__codelineno-2-53" href="#__codelineno-2-53"></a>
<a id="__codelineno-2-54" name="__codelineno-2-54" href="#__codelineno-2-54"></a><span class="w"> </span><span class="c1">// Page Header</span>
<a id="__codelineno-2-55" name="__codelineno-2-55" href="#__codelineno-2-55"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderHeader</span><span class="p">(),</span>
<a id="__codelineno-2-56" name="__codelineno-2-56" href="#__codelineno-2-56"></a>
<a id="__codelineno-2-57" name="__codelineno-2-57" href="#__codelineno-2-57"></a><span class="w"> </span><span class="c1">// Stats Overview Grid</span>
<a id="__codelineno-2-58" name="__codelineno-2-58" href="#__codelineno-2-58"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatsOverview</span><span class="p">(),</span>
<a id="__codelineno-2-59" name="__codelineno-2-59" href="#__codelineno-2-59"></a>
<a id="__codelineno-2-60" name="__codelineno-2-60" href="#__codelineno-2-60"></a><span class="w"> </span><span class="c1">// Content Cards</span>
<a id="__codelineno-2-61" name="__codelineno-2-61" href="#__codelineno-2-61"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderContent</span><span class="p">()</span>
<a id="__codelineno-2-62" name="__codelineno-2-62" href="#__codelineno-2-62"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-63" name="__codelineno-2-63" href="#__codelineno-2-63"></a>
<a id="__codelineno-2-64" name="__codelineno-2-64" href="#__codelineno-2-64"></a><span class="w"> </span><span class="c1">// Setup auto-refresh polling (30 seconds)</span>
<a id="__codelineno-2-65" name="__codelineno-2-65" href="#__codelineno-2-65"></a><span class="w"> </span><span class="nx">poll</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-66" name="__codelineno-2-66" href="#__codelineno-2-66"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
<a id="__codelineno-2-67" name="__codelineno-2-67" href="#__codelineno-2-67"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getStatus</span><span class="p">(),</span>
<a id="__codelineno-2-68" name="__codelineno-2-68" href="#__codelineno-2-68"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getData</span><span class="p">()</span>
<a id="__codelineno-2-69" name="__codelineno-2-69" href="#__codelineno-2-69"></a><span class="w"> </span><span class="p">]).</span><span class="nx">then</span><span class="p">(</span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">refreshData</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-70" name="__codelineno-2-70" href="#__codelineno-2-70"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">refreshData</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{};</span>
<a id="__codelineno-2-71" name="__codelineno-2-71" href="#__codelineno-2-71"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">componentData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">refreshData</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">};</span>
<a id="__codelineno-2-72" name="__codelineno-2-72" href="#__codelineno-2-72"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">updateDashboard</span><span class="p">();</span>
<a id="__codelineno-2-73" name="__codelineno-2-73" href="#__codelineno-2-73"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="k">this</span><span class="p">));</span>
<a id="__codelineno-2-74" name="__codelineno-2-74" href="#__codelineno-2-74"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="k">this</span><span class="p">),</span><span class="w"> </span><span class="mf">30</span><span class="p">);</span>
<a id="__codelineno-2-75" name="__codelineno-2-75" href="#__codelineno-2-75"></a>
<a id="__codelineno-2-76" name="__codelineno-2-76" href="#__codelineno-2-76"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">container</span><span class="p">;</span>
<a id="__codelineno-2-77" name="__codelineno-2-77" href="#__codelineno-2-77"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-78" name="__codelineno-2-78" href="#__codelineno-2-78"></a>
<a id="__codelineno-2-79" name="__codelineno-2-79" href="#__codelineno-2-79"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-80" name="__codelineno-2-80" href="#__codelineno-2-80"></a><span class="w"> </span><span class="c1">// Render Components</span>
<a id="__codelineno-2-81" name="__codelineno-2-81" href="#__codelineno-2-81"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-82" name="__codelineno-2-82" href="#__codelineno-2-82"></a>
<a id="__codelineno-2-83" name="__codelineno-2-83" href="#__codelineno-2-83"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-84" name="__codelineno-2-84" href="#__codelineno-2-84"></a><span class="cm"> * Render page header with title and stats badges</span>
<a id="__codelineno-2-85" name="__codelineno-2-85" href="#__codelineno-2-85"></a><span class="cm"> */</span>
<a id="__codelineno-2-86" name="__codelineno-2-86" href="#__codelineno-2-86"></a><span class="w"> </span><span class="nx">renderHeader</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-87" name="__codelineno-2-87" href="#__codelineno-2-87"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-page-header&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-88" name="__codelineno-2-88" href="#__codelineno-2-88"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-89" name="__codelineno-2-89" href="#__codelineno-2-89"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;h2&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-page-title&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-90" name="__codelineno-2-90" href="#__codelineno-2-90"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-page-title-icon&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;🚀&#39;</span><span class="p">),</span>
<a id="__codelineno-2-91" name="__codelineno-2-91" href="#__codelineno-2-91"></a><span class="w"> </span><span class="s1">&#39;Module Title&#39;</span>
<a id="__codelineno-2-92" name="__codelineno-2-92" href="#__codelineno-2-92"></a><span class="w"> </span><span class="p">]),</span>
<a id="__codelineno-2-93" name="__codelineno-2-93" href="#__codelineno-2-93"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-page-subtitle&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;Module description and purpose&#39;</span><span class="p">)</span>
<a id="__codelineno-2-94" name="__codelineno-2-94" href="#__codelineno-2-94"></a><span class="w"> </span><span class="p">]),</span>
<a id="__codelineno-2-95" name="__codelineno-2-95" href="#__codelineno-2-95"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-stats-grid&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-96" name="__codelineno-2-96" href="#__codelineno-2-96"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Active Items&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">active</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span>
<a id="__codelineno-2-97" name="__codelineno-2-97" href="#__codelineno-2-97"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Total Items&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">total</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span>
<a id="__codelineno-2-98" name="__codelineno-2-98" href="#__codelineno-2-98"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Status&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown&#39;</span><span class="p">),</span>
<a id="__codelineno-2-99" name="__codelineno-2-99" href="#__codelineno-2-99"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Version&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">version</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;1.0.0&#39;</span><span class="p">)</span>
<a id="__codelineno-2-100" name="__codelineno-2-100" href="#__codelineno-2-100"></a><span class="w"> </span><span class="p">])</span>
<a id="__codelineno-2-101" name="__codelineno-2-101" href="#__codelineno-2-101"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-102" name="__codelineno-2-102" href="#__codelineno-2-102"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-103" name="__codelineno-2-103" href="#__codelineno-2-103"></a>
<a id="__codelineno-2-104" name="__codelineno-2-104" href="#__codelineno-2-104"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-105" name="__codelineno-2-105" href="#__codelineno-2-105"></a><span class="cm"> * Render a single stat badge</span>
<a id="__codelineno-2-106" name="__codelineno-2-106" href="#__codelineno-2-106"></a><span class="cm"> */</span>
<a id="__codelineno-2-107" name="__codelineno-2-107" href="#__codelineno-2-107"></a><span class="w"> </span><span class="nx">renderStatBadge</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">label</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-108" name="__codelineno-2-108" href="#__codelineno-2-108"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-stat-badge&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-109" name="__codelineno-2-109" href="#__codelineno-2-109"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-stat-value&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nb">String</span><span class="p">(</span><span class="nx">value</span><span class="p">)),</span>
<a id="__codelineno-2-110" name="__codelineno-2-110" href="#__codelineno-2-110"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-stat-label&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">label</span><span class="p">)</span>
<a id="__codelineno-2-111" name="__codelineno-2-111" href="#__codelineno-2-111"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-112" name="__codelineno-2-112" href="#__codelineno-2-112"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-113" name="__codelineno-2-113" href="#__codelineno-2-113"></a>
<a id="__codelineno-2-114" name="__codelineno-2-114" href="#__codelineno-2-114"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-115" name="__codelineno-2-115" href="#__codelineno-2-115"></a><span class="cm"> * Render stats overview grid</span>
<a id="__codelineno-2-116" name="__codelineno-2-116" href="#__codelineno-2-116"></a><span class="cm"> */</span>
<a id="__codelineno-2-117" name="__codelineno-2-117" href="#__codelineno-2-117"></a><span class="w"> </span><span class="nx">renderStatsOverview</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-118" name="__codelineno-2-118" href="#__codelineno-2-118"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;stats-grid&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-119" name="__codelineno-2-119" href="#__codelineno-2-119"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderMetricCard</span><span class="p">(</span><span class="s1">&#39;CPU&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">cpu</span><span class="p">),</span>
<a id="__codelineno-2-120" name="__codelineno-2-120" href="#__codelineno-2-120"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderMetricCard</span><span class="p">(</span><span class="s1">&#39;Memory&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">memory</span><span class="p">),</span>
<a id="__codelineno-2-121" name="__codelineno-2-121" href="#__codelineno-2-121"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderMetricCard</span><span class="p">(</span><span class="s1">&#39;Disk&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">disk</span><span class="p">)</span>
<a id="__codelineno-2-122" name="__codelineno-2-122" href="#__codelineno-2-122"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-123" name="__codelineno-2-123" href="#__codelineno-2-123"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-124" name="__codelineno-2-124" href="#__codelineno-2-124"></a>
<a id="__codelineno-2-125" name="__codelineno-2-125" href="#__codelineno-2-125"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-126" name="__codelineno-2-126" href="#__codelineno-2-126"></a><span class="cm"> * Render a metric card with progress bar</span>
<a id="__codelineno-2-127" name="__codelineno-2-127" href="#__codelineno-2-127"></a><span class="cm"> */</span>
<a id="__codelineno-2-128" name="__codelineno-2-128" href="#__codelineno-2-128"></a><span class="w"> </span><span class="nx">renderMetricCard</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-129" name="__codelineno-2-129" href="#__codelineno-2-129"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">);</span>
<a id="__codelineno-2-130" name="__codelineno-2-130" href="#__codelineno-2-130"></a>
<a id="__codelineno-2-131" name="__codelineno-2-131" href="#__codelineno-2-131"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">data</span><span class="p">.</span><span class="nx">usage</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span>
<a id="__codelineno-2-132" name="__codelineno-2-132" href="#__codelineno-2-132"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mf">90</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;critical&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">usage</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mf">75</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;warning&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;ok&#39;</span><span class="p">);</span>
<a id="__codelineno-2-133" name="__codelineno-2-133" href="#__codelineno-2-133"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mf">90</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;#ef4444&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="p">(</span><span class="nx">usage</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mf">75</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;#f59e0b&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;#22c55e&#39;</span><span class="p">);</span>
<a id="__codelineno-2-134" name="__codelineno-2-134" href="#__codelineno-2-134"></a>
<a id="__codelineno-2-135" name="__codelineno-2-135" href="#__codelineno-2-135"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-card sh-metric-&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">status</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-136" name="__codelineno-2-136" href="#__codelineno-2-136"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-header&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-137" name="__codelineno-2-137" href="#__codelineno-2-137"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-icon&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">getMetricIcon</span><span class="p">(</span><span class="nx">title</span><span class="p">)),</span>
<a id="__codelineno-2-138" name="__codelineno-2-138" href="#__codelineno-2-138"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-title&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">title</span><span class="p">)</span>
<a id="__codelineno-2-139" name="__codelineno-2-139" href="#__codelineno-2-139"></a><span class="w"> </span><span class="p">]),</span>
<a id="__codelineno-2-140" name="__codelineno-2-140" href="#__codelineno-2-140"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-value&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;%&#39;</span><span class="p">),</span>
<a id="__codelineno-2-141" name="__codelineno-2-141" href="#__codelineno-2-141"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-progress&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-142" name="__codelineno-2-142" href="#__codelineno-2-142"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-143" name="__codelineno-2-143" href="#__codelineno-2-143"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-progress-bar&#39;</span><span class="p">,</span>
<a id="__codelineno-2-144" name="__codelineno-2-144" href="#__codelineno-2-144"></a><span class="w"> </span><span class="s1">&#39;style&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;width: &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s1">&#39;%; background: &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">color</span>
<a id="__codelineno-2-145" name="__codelineno-2-145" href="#__codelineno-2-145"></a><span class="w"> </span><span class="p">})</span>
<a id="__codelineno-2-146" name="__codelineno-2-146" href="#__codelineno-2-146"></a><span class="w"> </span><span class="p">]),</span>
<a id="__codelineno-2-147" name="__codelineno-2-147" href="#__codelineno-2-147"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-metric-details&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">data</span><span class="p">.</span><span class="nx">details</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;N/A&#39;</span><span class="p">)</span>
<a id="__codelineno-2-148" name="__codelineno-2-148" href="#__codelineno-2-148"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-149" name="__codelineno-2-149" href="#__codelineno-2-149"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-150" name="__codelineno-2-150" href="#__codelineno-2-150"></a>
<a id="__codelineno-2-151" name="__codelineno-2-151" href="#__codelineno-2-151"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-152" name="__codelineno-2-152" href="#__codelineno-2-152"></a><span class="cm"> * Get icon for metric type</span>
<a id="__codelineno-2-153" name="__codelineno-2-153" href="#__codelineno-2-153"></a><span class="cm"> */</span>
<a id="__codelineno-2-154" name="__codelineno-2-154" href="#__codelineno-2-154"></a><span class="w"> </span><span class="nx">getMetricIcon</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-155" name="__codelineno-2-155" href="#__codelineno-2-155"></a><span class="w"> </span><span class="k">switch</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-156" name="__codelineno-2-156" href="#__codelineno-2-156"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;CPU&#39;</span><span class="o">:</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;🔥&#39;</span><span class="p">;</span>
<a id="__codelineno-2-157" name="__codelineno-2-157" href="#__codelineno-2-157"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;Memory&#39;</span><span class="o">:</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;💾&#39;</span><span class="p">;</span>
<a id="__codelineno-2-158" name="__codelineno-2-158" href="#__codelineno-2-158"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;Disk&#39;</span><span class="o">:</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;💿&#39;</span><span class="p">;</span>
<a id="__codelineno-2-159" name="__codelineno-2-159" href="#__codelineno-2-159"></a><span class="w"> </span><span class="k">default</span><span class="o">:</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s1">&#39;📊&#39;</span><span class="p">;</span>
<a id="__codelineno-2-160" name="__codelineno-2-160" href="#__codelineno-2-160"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-161" name="__codelineno-2-161" href="#__codelineno-2-161"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-162" name="__codelineno-2-162" href="#__codelineno-2-162"></a>
<a id="__codelineno-2-163" name="__codelineno-2-163" href="#__codelineno-2-163"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-164" name="__codelineno-2-164" href="#__codelineno-2-164"></a><span class="cm"> * Render main content</span>
<a id="__codelineno-2-165" name="__codelineno-2-165" href="#__codelineno-2-165"></a><span class="cm"> */</span>
<a id="__codelineno-2-166" name="__codelineno-2-166" href="#__codelineno-2-166"></a><span class="w"> </span><span class="nx">renderContent</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-167" name="__codelineno-2-167" href="#__codelineno-2-167"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;content-grid&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-168" name="__codelineno-2-168" href="#__codelineno-2-168"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderCard</span><span class="p">(</span><span class="s1">&#39;Active Components&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderComponentsList</span><span class="p">()),</span>
<a id="__codelineno-2-169" name="__codelineno-2-169" href="#__codelineno-2-169"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderCard</span><span class="p">(</span><span class="s1">&#39;Quick Actions&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderQuickActions</span><span class="p">()),</span>
<a id="__codelineno-2-170" name="__codelineno-2-170" href="#__codelineno-2-170"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderCard</span><span class="p">(</span><span class="s1">&#39;Recent Activity&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderActivityLog</span><span class="p">())</span>
<a id="__codelineno-2-171" name="__codelineno-2-171" href="#__codelineno-2-171"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-172" name="__codelineno-2-172" href="#__codelineno-2-172"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-173" name="__codelineno-2-173" href="#__codelineno-2-173"></a>
<a id="__codelineno-2-174" name="__codelineno-2-174" href="#__codelineno-2-174"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-175" name="__codelineno-2-175" href="#__codelineno-2-175"></a><span class="cm"> * Render a card container</span>
<a id="__codelineno-2-176" name="__codelineno-2-176" href="#__codelineno-2-176"></a><span class="cm"> */</span>
<a id="__codelineno-2-177" name="__codelineno-2-177" href="#__codelineno-2-177"></a><span class="w"> </span><span class="nx">renderCard</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span><span class="w"> </span><span class="nx">content</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-178" name="__codelineno-2-178" href="#__codelineno-2-178"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-card&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-179" name="__codelineno-2-179" href="#__codelineno-2-179"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-card-header&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-180" name="__codelineno-2-180" href="#__codelineno-2-180"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;h3&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-card-title&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">title</span><span class="p">)</span>
<a id="__codelineno-2-181" name="__codelineno-2-181" href="#__codelineno-2-181"></a><span class="w"> </span><span class="p">]),</span>
<a id="__codelineno-2-182" name="__codelineno-2-182" href="#__codelineno-2-182"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-card-body&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">content</span><span class="p">)</span>
<a id="__codelineno-2-183" name="__codelineno-2-183" href="#__codelineno-2-183"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-184" name="__codelineno-2-184" href="#__codelineno-2-184"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-185" name="__codelineno-2-185" href="#__codelineno-2-185"></a>
<a id="__codelineno-2-186" name="__codelineno-2-186" href="#__codelineno-2-186"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-187" name="__codelineno-2-187" href="#__codelineno-2-187"></a><span class="cm"> * Render components list</span>
<a id="__codelineno-2-188" name="__codelineno-2-188" href="#__codelineno-2-188"></a><span class="cm"> */</span>
<a id="__codelineno-2-189" name="__codelineno-2-189" href="#__codelineno-2-189"></a><span class="w"> </span><span class="nx">renderComponentsList</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-190" name="__codelineno-2-190" href="#__codelineno-2-190"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">items</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">componentData</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">[];</span>
<a id="__codelineno-2-191" name="__codelineno-2-191" href="#__codelineno-2-191"></a>
<a id="__codelineno-2-192" name="__codelineno-2-192" href="#__codelineno-2-192"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">items</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-193" name="__codelineno-2-193" href="#__codelineno-2-193"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-empty-state&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-194" name="__codelineno-2-194" href="#__codelineno-2-194"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-empty-icon&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;📭&#39;</span><span class="p">),</span>
<a id="__codelineno-2-195" name="__codelineno-2-195" href="#__codelineno-2-195"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-empty-text&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;No components found&#39;</span><span class="p">)</span>
<a id="__codelineno-2-196" name="__codelineno-2-196" href="#__codelineno-2-196"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-197" name="__codelineno-2-197" href="#__codelineno-2-197"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-198" name="__codelineno-2-198" href="#__codelineno-2-198"></a>
<a id="__codelineno-2-199" name="__codelineno-2-199" href="#__codelineno-2-199"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;component-list&#39;</span><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-200" name="__codelineno-2-200" href="#__codelineno-2-200"></a><span class="w"> </span><span class="nx">items</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-201" name="__codelineno-2-201" href="#__codelineno-2-201"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderComponentItem</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
<a id="__codelineno-2-202" name="__codelineno-2-202" href="#__codelineno-2-202"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="k">this</span><span class="p">))</span>
<a id="__codelineno-2-203" name="__codelineno-2-203" href="#__codelineno-2-203"></a><span class="w"> </span><span class="p">);</span>
<a id="__codelineno-2-204" name="__codelineno-2-204" href="#__codelineno-2-204"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-205" name="__codelineno-2-205" href="#__codelineno-2-205"></a>
<a id="__codelineno-2-206" name="__codelineno-2-206" href="#__codelineno-2-206"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-207" name="__codelineno-2-207" href="#__codelineno-2-207"></a><span class="cm"> * Render a single component item</span>
<a id="__codelineno-2-208" name="__codelineno-2-208" href="#__codelineno-2-208"></a><span class="cm"> */</span>
<a id="__codelineno-2-209" name="__codelineno-2-209" href="#__codelineno-2-209"></a><span class="w"> </span><span class="nx">renderComponentItem</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-210" name="__codelineno-2-210" href="#__codelineno-2-210"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">statusClass</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">item</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s1">&#39;active&#39;</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s1">&#39;sh-card-success&#39;</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-card-warning&#39;</span><span class="p">;</span>
<a id="__codelineno-2-211" name="__codelineno-2-211" href="#__codelineno-2-211"></a>
<a id="__codelineno-2-212" name="__codelineno-2-212" href="#__codelineno-2-212"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;component-item &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">statusClass</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-213" name="__codelineno-2-213" href="#__codelineno-2-213"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;component-name&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown&#39;</span><span class="p">),</span>
<a id="__codelineno-2-214" name="__codelineno-2-214" href="#__codelineno-2-214"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;component-status&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">item</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;unknown&#39;</span><span class="p">),</span>
<a id="__codelineno-2-215" name="__codelineno-2-215" href="#__codelineno-2-215"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;component-actions&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-216" name="__codelineno-2-216" href="#__codelineno-2-216"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;button&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-217" name="__codelineno-2-217" href="#__codelineno-2-217"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-btn sh-btn-primary sh-btn-sm&#39;</span><span class="p">,</span>
<a id="__codelineno-2-218" name="__codelineno-2-218" href="#__codelineno-2-218"></a><span class="w"> </span><span class="s1">&#39;click&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleAction</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;view&#39;</span><span class="p">)</span>
<a id="__codelineno-2-219" name="__codelineno-2-219" href="#__codelineno-2-219"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;View&#39;</span><span class="p">),</span>
<a id="__codelineno-2-220" name="__codelineno-2-220" href="#__codelineno-2-220"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;button&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-221" name="__codelineno-2-221" href="#__codelineno-2-221"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-btn sh-btn-secondary sh-btn-sm&#39;</span><span class="p">,</span>
<a id="__codelineno-2-222" name="__codelineno-2-222" href="#__codelineno-2-222"></a><span class="w"> </span><span class="s1">&#39;click&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleAction</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;configure&#39;</span><span class="p">)</span>
<a id="__codelineno-2-223" name="__codelineno-2-223" href="#__codelineno-2-223"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;Configure&#39;</span><span class="p">)</span>
<a id="__codelineno-2-224" name="__codelineno-2-224" href="#__codelineno-2-224"></a><span class="w"> </span><span class="p">])</span>
<a id="__codelineno-2-225" name="__codelineno-2-225" href="#__codelineno-2-225"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-226" name="__codelineno-2-226" href="#__codelineno-2-226"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-227" name="__codelineno-2-227" href="#__codelineno-2-227"></a>
<a id="__codelineno-2-228" name="__codelineno-2-228" href="#__codelineno-2-228"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-229" name="__codelineno-2-229" href="#__codelineno-2-229"></a><span class="cm"> * Render quick actions</span>
<a id="__codelineno-2-230" name="__codelineno-2-230" href="#__codelineno-2-230"></a><span class="cm"> */</span>
<a id="__codelineno-2-231" name="__codelineno-2-231" href="#__codelineno-2-231"></a><span class="w"> </span><span class="nx">renderQuickActions</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-232" name="__codelineno-2-232" href="#__codelineno-2-232"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;quick-actions&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-233" name="__codelineno-2-233" href="#__codelineno-2-233"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;button&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-234" name="__codelineno-2-234" href="#__codelineno-2-234"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-btn sh-btn-primary&#39;</span><span class="p">,</span>
<a id="__codelineno-2-235" name="__codelineno-2-235" href="#__codelineno-2-235"></a><span class="w"> </span><span class="s1">&#39;click&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleRefresh</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">)</span>
<a id="__codelineno-2-236" name="__codelineno-2-236" href="#__codelineno-2-236"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;🔄 Refresh&#39;</span><span class="p">),</span>
<a id="__codelineno-2-237" name="__codelineno-2-237" href="#__codelineno-2-237"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;button&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-238" name="__codelineno-2-238" href="#__codelineno-2-238"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-btn sh-btn-success&#39;</span><span class="p">,</span>
<a id="__codelineno-2-239" name="__codelineno-2-239" href="#__codelineno-2-239"></a><span class="w"> </span><span class="s1">&#39;click&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleAction</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;start_all&#39;</span><span class="p">)</span>
<a id="__codelineno-2-240" name="__codelineno-2-240" href="#__codelineno-2-240"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;▶️ Start All&#39;</span><span class="p">),</span>
<a id="__codelineno-2-241" name="__codelineno-2-241" href="#__codelineno-2-241"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;button&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-242" name="__codelineno-2-242" href="#__codelineno-2-242"></a><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-btn sh-btn-danger&#39;</span><span class="p">,</span>
<a id="__codelineno-2-243" name="__codelineno-2-243" href="#__codelineno-2-243"></a><span class="w"> </span><span class="s1">&#39;click&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">handleAction</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">,</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;stop_all&#39;</span><span class="p">)</span>
<a id="__codelineno-2-244" name="__codelineno-2-244" href="#__codelineno-2-244"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;⏹️ Stop All&#39;</span><span class="p">)</span>
<a id="__codelineno-2-245" name="__codelineno-2-245" href="#__codelineno-2-245"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-246" name="__codelineno-2-246" href="#__codelineno-2-246"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-247" name="__codelineno-2-247" href="#__codelineno-2-247"></a>
<a id="__codelineno-2-248" name="__codelineno-2-248" href="#__codelineno-2-248"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-249" name="__codelineno-2-249" href="#__codelineno-2-249"></a><span class="cm"> * Render activity log</span>
<a id="__codelineno-2-250" name="__codelineno-2-250" href="#__codelineno-2-250"></a><span class="cm"> */</span>
<a id="__codelineno-2-251" name="__codelineno-2-251" href="#__codelineno-2-251"></a><span class="w"> </span><span class="nx">renderActivityLog</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-252" name="__codelineno-2-252" href="#__codelineno-2-252"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">activities</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">recent_activities</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">[];</span>
<a id="__codelineno-2-253" name="__codelineno-2-253" href="#__codelineno-2-253"></a>
<a id="__codelineno-2-254" name="__codelineno-2-254" href="#__codelineno-2-254"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">activities</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-255" name="__codelineno-2-255" href="#__codelineno-2-255"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;sh-empty-text&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="s1">&#39;No recent activity&#39;</span><span class="p">);</span>
<a id="__codelineno-2-256" name="__codelineno-2-256" href="#__codelineno-2-256"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-257" name="__codelineno-2-257" href="#__codelineno-2-257"></a>
<a id="__codelineno-2-258" name="__codelineno-2-258" href="#__codelineno-2-258"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;activity-log&#39;</span><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-259" name="__codelineno-2-259" href="#__codelineno-2-259"></a><span class="w"> </span><span class="nx">activities</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">activity</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-260" name="__codelineno-2-260" href="#__codelineno-2-260"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;activity-item&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-261" name="__codelineno-2-261" href="#__codelineno-2-261"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;activity-time&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">activity</span><span class="p">.</span><span class="nx">time</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">),</span>
<a id="__codelineno-2-262" name="__codelineno-2-262" href="#__codelineno-2-262"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;class&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;activity-text&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">activity</span><span class="p">.</span><span class="nx">message</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">)</span>
<a id="__codelineno-2-263" name="__codelineno-2-263" href="#__codelineno-2-263"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-264" name="__codelineno-2-264" href="#__codelineno-2-264"></a><span class="w"> </span><span class="p">})</span>
<a id="__codelineno-2-265" name="__codelineno-2-265" href="#__codelineno-2-265"></a><span class="w"> </span><span class="p">);</span>
<a id="__codelineno-2-266" name="__codelineno-2-266" href="#__codelineno-2-266"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-267" name="__codelineno-2-267" href="#__codelineno-2-267"></a>
<a id="__codelineno-2-268" name="__codelineno-2-268" href="#__codelineno-2-268"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-269" name="__codelineno-2-269" href="#__codelineno-2-269"></a><span class="w"> </span><span class="c1">// Event Handlers</span>
<a id="__codelineno-2-270" name="__codelineno-2-270" href="#__codelineno-2-270"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-271" name="__codelineno-2-271" href="#__codelineno-2-271"></a>
<a id="__codelineno-2-272" name="__codelineno-2-272" href="#__codelineno-2-272"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-273" name="__codelineno-2-273" href="#__codelineno-2-273"></a><span class="cm"> * Handle generic action</span>
<a id="__codelineno-2-274" name="__codelineno-2-274" href="#__codelineno-2-274"></a><span class="cm"> */</span>
<a id="__codelineno-2-275" name="__codelineno-2-275" href="#__codelineno-2-275"></a><span class="w"> </span><span class="nx">handleAction</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">action</span><span class="p">,</span><span class="w"> </span><span class="nx">ev</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-276" name="__codelineno-2-276" href="#__codelineno-2-276"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">;</span>
<a id="__codelineno-2-277" name="__codelineno-2-277" href="#__codelineno-2-277"></a>
<a id="__codelineno-2-278" name="__codelineno-2-278" href="#__codelineno-2-278"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">showModal</span><span class="p">(</span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Performing Action&#39;</span><span class="p">),</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-279" name="__codelineno-2-279" href="#__codelineno-2-279"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Please wait...&#39;</span><span class="p">))</span>
<a id="__codelineno-2-280" name="__codelineno-2-280" href="#__codelineno-2-280"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-281" name="__codelineno-2-281" href="#__codelineno-2-281"></a>
<a id="__codelineno-2-282" name="__codelineno-2-282" href="#__codelineno-2-282"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">performAction</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-283" name="__codelineno-2-283" href="#__codelineno-2-283"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">hideModal</span><span class="p">();</span>
<a id="__codelineno-2-284" name="__codelineno-2-284" href="#__codelineno-2-284"></a>
<a id="__codelineno-2-285" name="__codelineno-2-285" href="#__codelineno-2-285"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-286" name="__codelineno-2-286" href="#__codelineno-2-286"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">addNotification</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Action completed successfully&#39;</span><span class="p">)),</span><span class="w"> </span><span class="s1">&#39;success&#39;</span><span class="p">);</span>
<a id="__codelineno-2-287" name="__codelineno-2-287" href="#__codelineno-2-287"></a><span class="w"> </span><span class="nx">self</span><span class="p">.</span><span class="nx">handleRefresh</span><span class="p">();</span>
<a id="__codelineno-2-288" name="__codelineno-2-288" href="#__codelineno-2-288"></a><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-289" name="__codelineno-2-289" href="#__codelineno-2-289"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">addNotification</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Action failed: %s&#39;</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">message</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown error&#39;</span><span class="p">)),</span><span class="w"> </span><span class="s1">&#39;error&#39;</span><span class="p">);</span>
<a id="__codelineno-2-290" name="__codelineno-2-290" href="#__codelineno-2-290"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-291" name="__codelineno-2-291" href="#__codelineno-2-291"></a><span class="w"> </span><span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-292" name="__codelineno-2-292" href="#__codelineno-2-292"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">hideModal</span><span class="p">();</span>
<a id="__codelineno-2-293" name="__codelineno-2-293" href="#__codelineno-2-293"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">addNotification</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Error: %s&#39;</span><span class="p">).</span><span class="nx">format</span><span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown error&#39;</span><span class="p">)),</span><span class="w"> </span><span class="s1">&#39;error&#39;</span><span class="p">);</span>
<a id="__codelineno-2-294" name="__codelineno-2-294" href="#__codelineno-2-294"></a><span class="w"> </span><span class="p">});</span>
<a id="__codelineno-2-295" name="__codelineno-2-295" href="#__codelineno-2-295"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-296" name="__codelineno-2-296" href="#__codelineno-2-296"></a>
<a id="__codelineno-2-297" name="__codelineno-2-297" href="#__codelineno-2-297"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-298" name="__codelineno-2-298" href="#__codelineno-2-298"></a><span class="cm"> * Handle refresh</span>
<a id="__codelineno-2-299" name="__codelineno-2-299" href="#__codelineno-2-299"></a><span class="cm"> */</span>
<a id="__codelineno-2-300" name="__codelineno-2-300" href="#__codelineno-2-300"></a><span class="w"> </span><span class="nx">handleRefresh</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-301" name="__codelineno-2-301" href="#__codelineno-2-301"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">;</span>
<a id="__codelineno-2-302" name="__codelineno-2-302" href="#__codelineno-2-302"></a>
<a id="__codelineno-2-303" name="__codelineno-2-303" href="#__codelineno-2-303"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
<a id="__codelineno-2-304" name="__codelineno-2-304" href="#__codelineno-2-304"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getStatus</span><span class="p">(),</span>
<a id="__codelineno-2-305" name="__codelineno-2-305" href="#__codelineno-2-305"></a><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getData</span><span class="p">()</span>
<a id="__codelineno-2-306" name="__codelineno-2-306" href="#__codelineno-2-306"></a><span class="w"> </span><span class="p">]).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-307" name="__codelineno-2-307" href="#__codelineno-2-307"></a><span class="w"> </span><span class="nx">self</span><span class="p">.</span><span class="nx">statusData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{};</span>
<a id="__codelineno-2-308" name="__codelineno-2-308" href="#__codelineno-2-308"></a><span class="w"> </span><span class="nx">self</span><span class="p">.</span><span class="nx">componentData</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">data</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">data</span><span class="o">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">};</span>
<a id="__codelineno-2-309" name="__codelineno-2-309" href="#__codelineno-2-309"></a><span class="w"> </span><span class="nx">self</span><span class="p">.</span><span class="nx">updateDashboard</span><span class="p">();</span>
<a id="__codelineno-2-310" name="__codelineno-2-310" href="#__codelineno-2-310"></a><span class="w"> </span><span class="nx">ui</span><span class="p">.</span><span class="nx">addNotification</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="p">(</span><span class="s1">&#39;Dashboard refreshed&#39;</span><span class="p">)),</span><span class="w"> </span><span class="s1">&#39;info&#39;</span><span class="p">);</span>
<a id="__codelineno-2-311" name="__codelineno-2-311" href="#__codelineno-2-311"></a><span class="w"> </span><span class="p">});</span>
<a id="__codelineno-2-312" name="__codelineno-2-312" href="#__codelineno-2-312"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-313" name="__codelineno-2-313" href="#__codelineno-2-313"></a>
<a id="__codelineno-2-314" name="__codelineno-2-314" href="#__codelineno-2-314"></a><span class="w"> </span><span class="cm">/**</span>
<a id="__codelineno-2-315" name="__codelineno-2-315" href="#__codelineno-2-315"></a><span class="cm"> * Update dashboard without full re-render</span>
<a id="__codelineno-2-316" name="__codelineno-2-316" href="#__codelineno-2-316"></a><span class="cm"> */</span>
<a id="__codelineno-2-317" name="__codelineno-2-317" href="#__codelineno-2-317"></a><span class="w"> </span><span class="nx">updateDashboard</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-318" name="__codelineno-2-318" href="#__codelineno-2-318"></a><span class="w"> </span><span class="c1">// Update specific DOM elements instead of full re-render</span>
<a id="__codelineno-2-319" name="__codelineno-2-319" href="#__codelineno-2-319"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">statsGrid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;.sh-stats-grid&#39;</span><span class="p">);</span>
<a id="__codelineno-2-320" name="__codelineno-2-320" href="#__codelineno-2-320"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">statsGrid</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-321" name="__codelineno-2-321" href="#__codelineno-2-321"></a><span class="w"> </span><span class="nx">dom</span><span class="p">.</span><span class="nx">content</span><span class="p">(</span><span class="nx">statsGrid</span><span class="p">,</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-2-322" name="__codelineno-2-322" href="#__codelineno-2-322"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Active Items&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">active</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span>
<a id="__codelineno-2-323" name="__codelineno-2-323" href="#__codelineno-2-323"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Total Items&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">total</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span>
<a id="__codelineno-2-324" name="__codelineno-2-324" href="#__codelineno-2-324"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Status&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown&#39;</span><span class="p">),</span>
<a id="__codelineno-2-325" name="__codelineno-2-325" href="#__codelineno-2-325"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderStatBadge</span><span class="p">(</span><span class="s1">&#39;Version&#39;</span><span class="p">,</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">statusData</span><span class="p">.</span><span class="nx">version</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;1.0.0&#39;</span><span class="p">)</span>
<a id="__codelineno-2-326" name="__codelineno-2-326" href="#__codelineno-2-326"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-2-327" name="__codelineno-2-327" href="#__codelineno-2-327"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-328" name="__codelineno-2-328" href="#__codelineno-2-328"></a>
<a id="__codelineno-2-329" name="__codelineno-2-329" href="#__codelineno-2-329"></a><span class="w"> </span><span class="c1">// Update components list</span>
<a id="__codelineno-2-330" name="__codelineno-2-330" href="#__codelineno-2-330"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">componentsList</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;.component-list&#39;</span><span class="p">);</span>
<a id="__codelineno-2-331" name="__codelineno-2-331" href="#__codelineno-2-331"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">componentsList</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-332" name="__codelineno-2-332" href="#__codelineno-2-332"></a><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">items</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">componentData</span><span class="p">.</span><span class="nx">data</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="p">[];</span>
<a id="__codelineno-2-333" name="__codelineno-2-333" href="#__codelineno-2-333"></a><span class="w"> </span><span class="nx">dom</span><span class="p">.</span><span class="nx">content</span><span class="p">(</span><span class="nx">componentsList</span><span class="p">,</span>
<a id="__codelineno-2-334" name="__codelineno-2-334" href="#__codelineno-2-334"></a><span class="w"> </span><span class="nx">items</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">L</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-2-335" name="__codelineno-2-335" href="#__codelineno-2-335"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">renderComponentItem</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
<a id="__codelineno-2-336" name="__codelineno-2-336" href="#__codelineno-2-336"></a><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="k">this</span><span class="p">))</span>
<a id="__codelineno-2-337" name="__codelineno-2-337" href="#__codelineno-2-337"></a><span class="w"> </span><span class="p">);</span>
<a id="__codelineno-2-338" name="__codelineno-2-338" href="#__codelineno-2-338"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-2-339" name="__codelineno-2-339" href="#__codelineno-2-339"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-2-340" name="__codelineno-2-340" href="#__codelineno-2-340"></a>
<a id="__codelineno-2-341" name="__codelineno-2-341" href="#__codelineno-2-341"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-342" name="__codelineno-2-342" href="#__codelineno-2-342"></a><span class="w"> </span><span class="c1">// Required LuCI Methods (can be null if not used)</span>
<a id="__codelineno-2-343" name="__codelineno-2-343" href="#__codelineno-2-343"></a><span class="w"> </span><span class="c1">// ========================================================================</span>
<a id="__codelineno-2-344" name="__codelineno-2-344" href="#__codelineno-2-344"></a>
<a id="__codelineno-2-345" name="__codelineno-2-345" href="#__codelineno-2-345"></a><span class="w"> </span><span class="nx">handleSaveApply</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-2-346" name="__codelineno-2-346" href="#__codelineno-2-346"></a><span class="w"> </span><span class="nx">handleSave</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-2-347" name="__codelineno-2-347" href="#__codelineno-2-347"></a><span class="w"> </span><span class="nx">handleReset</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span>
<a id="__codelineno-2-348" name="__codelineno-2-348" href="#__codelineno-2-348"></a><span class="p">});</span>
</code></pre></div>
<p><strong>Key Points:</strong>
- Extend <code>view</code> with <code>load()</code> and <code>render()</code> methods
- Use <code>E()</code> helper to build DOM elements
- Use <code>L.bind()</code> for event handlers to preserve <code>this</code> context
- Use <code>poll.add()</code> for auto-refresh functionality
- Use <code>dom.content()</code> for efficient partial updates
- Use <code>ui.showModal()</code>, <code>ui.hideModal()</code>, <code>ui.addNotification()</code> for user feedback
- Always handle errors in Promise chains</p>
<hr />
<h2 id="rpcd-backend-template">RPCD Backend Template<a class="headerlink" href="#rpcd-backend-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>root/usr/libexec/rpcd/luci.&lt;module-name&gt;</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="ch">#!/bin/sh</span>
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="c1"># [Module Name] RPCD Backend</span>
<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="c1"># Package: luci-app-&lt;module-name&gt;</span>
<a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="c1"># Version: 1.0.0</span>
<a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a>
<a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="c1"># Source required libraries</span>
<a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a>.<span class="w"> </span>/lib/functions.sh
<a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a>.<span class="w"> </span>/usr/share/libubox/jshn.sh
<a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a>
<a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="c1"># ============================================================================</span>
<a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="c1"># RPC Methods</span>
<a id="__codelineno-3-12" name="__codelineno-3-12" href="#__codelineno-3-12"></a><span class="c1"># ============================================================================</span>
<a id="__codelineno-3-13" name="__codelineno-3-13" href="#__codelineno-3-13"></a>
<a id="__codelineno-3-14" name="__codelineno-3-14" href="#__codelineno-3-14"></a><span class="c1"># Get status information</span>
<a id="__codelineno-3-15" name="__codelineno-3-15" href="#__codelineno-3-15"></a>status<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<a id="__codelineno-3-16" name="__codelineno-3-16" href="#__codelineno-3-16"></a><span class="w"> </span>json_init
<a id="__codelineno-3-17" name="__codelineno-3-17" href="#__codelineno-3-17"></a>
<a id="__codelineno-3-18" name="__codelineno-3-18" href="#__codelineno-3-18"></a><span class="w"> </span><span class="c1"># Example: Get system info</span>
<a id="__codelineno-3-19" name="__codelineno-3-19" href="#__codelineno-3-19"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span><span class="nv">hostname</span><span class="o">=</span><span class="k">$(</span>cat<span class="w"> </span>/proc/sys/kernel/hostname<span class="w"> </span><span class="m">2</span>&gt;/dev/null<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;unknown&quot;</span><span class="k">)</span>
<a id="__codelineno-3-20" name="__codelineno-3-20" href="#__codelineno-3-20"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span><span class="nv">uptime</span><span class="o">=</span><span class="k">$(</span>awk<span class="w"> </span><span class="s1">&#39;{print int($1)}&#39;</span><span class="w"> </span>/proc/uptime<span class="w"> </span><span class="m">2</span>&gt;/dev/null<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="m">0</span><span class="k">)</span>
<a id="__codelineno-3-21" name="__codelineno-3-21" href="#__codelineno-3-21"></a>
<a id="__codelineno-3-22" name="__codelineno-3-22" href="#__codelineno-3-22"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;hostname&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$hostname</span><span class="s2">&quot;</span>
<a id="__codelineno-3-23" name="__codelineno-3-23" href="#__codelineno-3-23"></a><span class="w"> </span>json_add_int<span class="w"> </span><span class="s2">&quot;uptime&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$uptime</span><span class="s2">&quot;</span>
<a id="__codelineno-3-24" name="__codelineno-3-24" href="#__codelineno-3-24"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;version&quot;</span><span class="w"> </span><span class="s2">&quot;1.0.0&quot;</span>
<a id="__codelineno-3-25" name="__codelineno-3-25" href="#__codelineno-3-25"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;status&quot;</span><span class="w"> </span><span class="s2">&quot;running&quot;</span>
<a id="__codelineno-3-26" name="__codelineno-3-26" href="#__codelineno-3-26"></a>
<a id="__codelineno-3-27" name="__codelineno-3-27" href="#__codelineno-3-27"></a><span class="w"> </span><span class="c1"># Add nested object</span>
<a id="__codelineno-3-28" name="__codelineno-3-28" href="#__codelineno-3-28"></a><span class="w"> </span>json_add_object<span class="w"> </span><span class="s2">&quot;cpu&quot;</span>
<a id="__codelineno-3-29" name="__codelineno-3-29" href="#__codelineno-3-29"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span><span class="nv">cpu_load</span><span class="o">=</span><span class="k">$(</span>awk<span class="w"> </span><span class="s1">&#39;{print $1}&#39;</span><span class="w"> </span>/proc/loadavg<span class="w"> </span><span class="m">2</span>&gt;/dev/null<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;0&quot;</span><span class="k">)</span>
<a id="__codelineno-3-30" name="__codelineno-3-30" href="#__codelineno-3-30"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;load&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$cpu_load</span><span class="s2">&quot;</span>
<a id="__codelineno-3-31" name="__codelineno-3-31" href="#__codelineno-3-31"></a><span class="w"> </span>json_add_int<span class="w"> </span><span class="s2">&quot;usage&quot;</span><span class="w"> </span><span class="s2">&quot;45&quot;</span>
<a id="__codelineno-3-32" name="__codelineno-3-32" href="#__codelineno-3-32"></a><span class="w"> </span>json_close_object
<a id="__codelineno-3-33" name="__codelineno-3-33" href="#__codelineno-3-33"></a>
<a id="__codelineno-3-34" name="__codelineno-3-34" href="#__codelineno-3-34"></a><span class="w"> </span><span class="c1"># Add timestamp</span>
<a id="__codelineno-3-35" name="__codelineno-3-35" href="#__codelineno-3-35"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;timestamp&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="k">$(</span>date<span class="w"> </span><span class="s1">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="k">)</span><span class="s2">&quot;</span>
<a id="__codelineno-3-36" name="__codelineno-3-36" href="#__codelineno-3-36"></a>
<a id="__codelineno-3-37" name="__codelineno-3-37" href="#__codelineno-3-37"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-38" name="__codelineno-3-38" href="#__codelineno-3-38"></a><span class="o">}</span>
<a id="__codelineno-3-39" name="__codelineno-3-39" href="#__codelineno-3-39"></a>
<a id="__codelineno-3-40" name="__codelineno-3-40" href="#__codelineno-3-40"></a><span class="c1"># Get data with array</span>
<a id="__codelineno-3-41" name="__codelineno-3-41" href="#__codelineno-3-41"></a>get_data<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<a id="__codelineno-3-42" name="__codelineno-3-42" href="#__codelineno-3-42"></a><span class="w"> </span>json_init
<a id="__codelineno-3-43" name="__codelineno-3-43" href="#__codelineno-3-43"></a><span class="w"> </span>json_add_array<span class="w"> </span><span class="s2">&quot;data&quot;</span>
<a id="__codelineno-3-44" name="__codelineno-3-44" href="#__codelineno-3-44"></a>
<a id="__codelineno-3-45" name="__codelineno-3-45" href="#__codelineno-3-45"></a><span class="w"> </span><span class="c1"># Example: List files</span>
<a id="__codelineno-3-46" name="__codelineno-3-46" href="#__codelineno-3-46"></a><span class="w"> </span><span class="k">for</span><span class="w"> </span>file<span class="w"> </span><span class="k">in</span><span class="w"> </span>/etc/config/*<span class="p">;</span><span class="w"> </span><span class="k">do</span>
<a id="__codelineno-3-47" name="__codelineno-3-47" href="#__codelineno-3-47"></a><span class="w"> </span><span class="o">[</span><span class="w"> </span>-f<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="k">continue</span>
<a id="__codelineno-3-48" name="__codelineno-3-48" href="#__codelineno-3-48"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span><span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span><span class="k">)</span>
<a id="__codelineno-3-49" name="__codelineno-3-49" href="#__codelineno-3-49"></a>
<a id="__codelineno-3-50" name="__codelineno-3-50" href="#__codelineno-3-50"></a><span class="w"> </span>json_add_object<span class="w"> </span><span class="s2">&quot;&quot;</span>
<a id="__codelineno-3-51" name="__codelineno-3-51" href="#__codelineno-3-51"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;name&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$name</span><span class="s2">&quot;</span>
<a id="__codelineno-3-52" name="__codelineno-3-52" href="#__codelineno-3-52"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;path&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span>
<a id="__codelineno-3-53" name="__codelineno-3-53" href="#__codelineno-3-53"></a><span class="w"> </span>json_add_int<span class="w"> </span><span class="s2">&quot;size&quot;</span><span class="w"> </span><span class="s2">&quot;</span><span class="k">$(</span>stat<span class="w"> </span>-c%s<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$file</span><span class="s2">&quot;</span><span class="w"> </span><span class="m">2</span>&gt;/dev/null<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="m">0</span><span class="k">)</span><span class="s2">&quot;</span>
<a id="__codelineno-3-54" name="__codelineno-3-54" href="#__codelineno-3-54"></a><span class="w"> </span>json_close_object
<a id="__codelineno-3-55" name="__codelineno-3-55" href="#__codelineno-3-55"></a><span class="w"> </span><span class="k">done</span>
<a id="__codelineno-3-56" name="__codelineno-3-56" href="#__codelineno-3-56"></a>
<a id="__codelineno-3-57" name="__codelineno-3-57" href="#__codelineno-3-57"></a><span class="w"> </span>json_close_array
<a id="__codelineno-3-58" name="__codelineno-3-58" href="#__codelineno-3-58"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-59" name="__codelineno-3-59" href="#__codelineno-3-59"></a><span class="o">}</span>
<a id="__codelineno-3-60" name="__codelineno-3-60" href="#__codelineno-3-60"></a>
<a id="__codelineno-3-61" name="__codelineno-3-61" href="#__codelineno-3-61"></a><span class="c1"># Perform action with parameters</span>
<a id="__codelineno-3-62" name="__codelineno-3-62" href="#__codelineno-3-62"></a>perform_action<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<a id="__codelineno-3-63" name="__codelineno-3-63" href="#__codelineno-3-63"></a><span class="w"> </span><span class="c1"># Read JSON input from stdin</span>
<a id="__codelineno-3-64" name="__codelineno-3-64" href="#__codelineno-3-64"></a><span class="w"> </span><span class="nb">read</span><span class="w"> </span>-r<span class="w"> </span>input
<a id="__codelineno-3-65" name="__codelineno-3-65" href="#__codelineno-3-65"></a><span class="w"> </span>json_load<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$input</span><span class="s2">&quot;</span>
<a id="__codelineno-3-66" name="__codelineno-3-66" href="#__codelineno-3-66"></a>
<a id="__codelineno-3-67" name="__codelineno-3-67" href="#__codelineno-3-67"></a><span class="w"> </span><span class="c1"># Extract parameters</span>
<a id="__codelineno-3-68" name="__codelineno-3-68" href="#__codelineno-3-68"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span>action_type<span class="w"> </span>target
<a id="__codelineno-3-69" name="__codelineno-3-69" href="#__codelineno-3-69"></a><span class="w"> </span>json_get_var<span class="w"> </span>action_type<span class="w"> </span>action_type
<a id="__codelineno-3-70" name="__codelineno-3-70" href="#__codelineno-3-70"></a><span class="w"> </span>json_get_var<span class="w"> </span>target<span class="w"> </span>target
<a id="__codelineno-3-71" name="__codelineno-3-71" href="#__codelineno-3-71"></a><span class="w"> </span>json_cleanup
<a id="__codelineno-3-72" name="__codelineno-3-72" href="#__codelineno-3-72"></a>
<a id="__codelineno-3-73" name="__codelineno-3-73" href="#__codelineno-3-73"></a><span class="w"> </span><span class="c1"># Validate parameters</span>
<a id="__codelineno-3-74" name="__codelineno-3-74" href="#__codelineno-3-74"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$action_type</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<a id="__codelineno-3-75" name="__codelineno-3-75" href="#__codelineno-3-75"></a><span class="w"> </span>json_init
<a id="__codelineno-3-76" name="__codelineno-3-76" href="#__codelineno-3-76"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">0</span>
<a id="__codelineno-3-77" name="__codelineno-3-77" href="#__codelineno-3-77"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Action type is required&quot;</span>
<a id="__codelineno-3-78" name="__codelineno-3-78" href="#__codelineno-3-78"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-79" name="__codelineno-3-79" href="#__codelineno-3-79"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="m">1</span>
<a id="__codelineno-3-80" name="__codelineno-3-80" href="#__codelineno-3-80"></a><span class="w"> </span><span class="k">fi</span>
<a id="__codelineno-3-81" name="__codelineno-3-81" href="#__codelineno-3-81"></a>
<a id="__codelineno-3-82" name="__codelineno-3-82" href="#__codelineno-3-82"></a><span class="w"> </span><span class="c1"># Perform action based on type</span>
<a id="__codelineno-3-83" name="__codelineno-3-83" href="#__codelineno-3-83"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span><span class="nv">result</span><span class="o">=</span><span class="m">0</span>
<a id="__codelineno-3-84" name="__codelineno-3-84" href="#__codelineno-3-84"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$action_type</span><span class="s2">&quot;</span><span class="w"> </span><span class="k">in</span>
<a id="__codelineno-3-85" name="__codelineno-3-85" href="#__codelineno-3-85"></a><span class="w"> </span>start<span class="o">)</span>
<a id="__codelineno-3-86" name="__codelineno-3-86" href="#__codelineno-3-86"></a><span class="w"> </span><span class="c1"># Example: Start a service</span>
<a id="__codelineno-3-87" name="__codelineno-3-87" href="#__codelineno-3-87"></a><span class="w"> </span>/etc/init.d/<span class="s2">&quot;</span><span class="nv">$target</span><span class="s2">&quot;</span><span class="w"> </span>start<span class="w"> </span>&gt;/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span>
<a id="__codelineno-3-88" name="__codelineno-3-88" href="#__codelineno-3-88"></a><span class="w"> </span><span class="nv">result</span><span class="o">=</span><span class="nv">$?</span>
<a id="__codelineno-3-89" name="__codelineno-3-89" href="#__codelineno-3-89"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-90" name="__codelineno-3-90" href="#__codelineno-3-90"></a><span class="w"> </span>stop<span class="o">)</span>
<a id="__codelineno-3-91" name="__codelineno-3-91" href="#__codelineno-3-91"></a><span class="w"> </span><span class="c1"># Example: Stop a service</span>
<a id="__codelineno-3-92" name="__codelineno-3-92" href="#__codelineno-3-92"></a><span class="w"> </span>/etc/init.d/<span class="s2">&quot;</span><span class="nv">$target</span><span class="s2">&quot;</span><span class="w"> </span>stop<span class="w"> </span>&gt;/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span>
<a id="__codelineno-3-93" name="__codelineno-3-93" href="#__codelineno-3-93"></a><span class="w"> </span><span class="nv">result</span><span class="o">=</span><span class="nv">$?</span>
<a id="__codelineno-3-94" name="__codelineno-3-94" href="#__codelineno-3-94"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-95" name="__codelineno-3-95" href="#__codelineno-3-95"></a><span class="w"> </span>restart<span class="o">)</span>
<a id="__codelineno-3-96" name="__codelineno-3-96" href="#__codelineno-3-96"></a><span class="w"> </span><span class="c1"># Example: Restart a service</span>
<a id="__codelineno-3-97" name="__codelineno-3-97" href="#__codelineno-3-97"></a><span class="w"> </span>/etc/init.d/<span class="s2">&quot;</span><span class="nv">$target</span><span class="s2">&quot;</span><span class="w"> </span>restart<span class="w"> </span>&gt;/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span>
<a id="__codelineno-3-98" name="__codelineno-3-98" href="#__codelineno-3-98"></a><span class="w"> </span><span class="nv">result</span><span class="o">=</span><span class="nv">$?</span>
<a id="__codelineno-3-99" name="__codelineno-3-99" href="#__codelineno-3-99"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-100" name="__codelineno-3-100" href="#__codelineno-3-100"></a><span class="w"> </span>*<span class="o">)</span>
<a id="__codelineno-3-101" name="__codelineno-3-101" href="#__codelineno-3-101"></a><span class="w"> </span>json_init
<a id="__codelineno-3-102" name="__codelineno-3-102" href="#__codelineno-3-102"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">0</span>
<a id="__codelineno-3-103" name="__codelineno-3-103" href="#__codelineno-3-103"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Invalid action: </span><span class="nv">$action_type</span><span class="s2">&quot;</span>
<a id="__codelineno-3-104" name="__codelineno-3-104" href="#__codelineno-3-104"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-105" name="__codelineno-3-105" href="#__codelineno-3-105"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="m">1</span>
<a id="__codelineno-3-106" name="__codelineno-3-106" href="#__codelineno-3-106"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-107" name="__codelineno-3-107" href="#__codelineno-3-107"></a><span class="w"> </span><span class="k">esac</span>
<a id="__codelineno-3-108" name="__codelineno-3-108" href="#__codelineno-3-108"></a>
<a id="__codelineno-3-109" name="__codelineno-3-109" href="#__codelineno-3-109"></a><span class="w"> </span><span class="c1"># Return result</span>
<a id="__codelineno-3-110" name="__codelineno-3-110" href="#__codelineno-3-110"></a><span class="w"> </span>json_init
<a id="__codelineno-3-111" name="__codelineno-3-111" href="#__codelineno-3-111"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$result</span><span class="s2">&quot;</span><span class="w"> </span>-eq<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<a id="__codelineno-3-112" name="__codelineno-3-112" href="#__codelineno-3-112"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">1</span>
<a id="__codelineno-3-113" name="__codelineno-3-113" href="#__codelineno-3-113"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Action &#39;</span><span class="nv">$action_type</span><span class="s2">&#39; completed successfully&quot;</span>
<a id="__codelineno-3-114" name="__codelineno-3-114" href="#__codelineno-3-114"></a><span class="w"> </span><span class="k">else</span>
<a id="__codelineno-3-115" name="__codelineno-3-115" href="#__codelineno-3-115"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">0</span>
<a id="__codelineno-3-116" name="__codelineno-3-116" href="#__codelineno-3-116"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Action &#39;</span><span class="nv">$action_type</span><span class="s2">&#39; failed&quot;</span>
<a id="__codelineno-3-117" name="__codelineno-3-117" href="#__codelineno-3-117"></a><span class="w"> </span><span class="k">fi</span>
<a id="__codelineno-3-118" name="__codelineno-3-118" href="#__codelineno-3-118"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-119" name="__codelineno-3-119" href="#__codelineno-3-119"></a><span class="o">}</span>
<a id="__codelineno-3-120" name="__codelineno-3-120" href="#__codelineno-3-120"></a>
<a id="__codelineno-3-121" name="__codelineno-3-121" href="#__codelineno-3-121"></a><span class="c1"># Update configuration</span>
<a id="__codelineno-3-122" name="__codelineno-3-122" href="#__codelineno-3-122"></a>update_config<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<a id="__codelineno-3-123" name="__codelineno-3-123" href="#__codelineno-3-123"></a><span class="w"> </span><span class="nb">read</span><span class="w"> </span>-r<span class="w"> </span>input
<a id="__codelineno-3-124" name="__codelineno-3-124" href="#__codelineno-3-124"></a><span class="w"> </span>json_load<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$input</span><span class="s2">&quot;</span>
<a id="__codelineno-3-125" name="__codelineno-3-125" href="#__codelineno-3-125"></a>
<a id="__codelineno-3-126" name="__codelineno-3-126" href="#__codelineno-3-126"></a><span class="w"> </span><span class="nb">local</span><span class="w"> </span>key<span class="w"> </span>value<span class="w"> </span>persist
<a id="__codelineno-3-127" name="__codelineno-3-127" href="#__codelineno-3-127"></a><span class="w"> </span>json_get_var<span class="w"> </span>key<span class="w"> </span>key
<a id="__codelineno-3-128" name="__codelineno-3-128" href="#__codelineno-3-128"></a><span class="w"> </span>json_get_var<span class="w"> </span>value<span class="w"> </span>value
<a id="__codelineno-3-129" name="__codelineno-3-129" href="#__codelineno-3-129"></a><span class="w"> </span>json_get_var<span class="w"> </span>persist<span class="w"> </span>persist
<a id="__codelineno-3-130" name="__codelineno-3-130" href="#__codelineno-3-130"></a><span class="w"> </span>json_cleanup
<a id="__codelineno-3-131" name="__codelineno-3-131" href="#__codelineno-3-131"></a>
<a id="__codelineno-3-132" name="__codelineno-3-132" href="#__codelineno-3-132"></a><span class="w"> </span><span class="c1"># Validate</span>
<a id="__codelineno-3-133" name="__codelineno-3-133" href="#__codelineno-3-133"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$key</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$value</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<a id="__codelineno-3-134" name="__codelineno-3-134" href="#__codelineno-3-134"></a><span class="w"> </span>json_init
<a id="__codelineno-3-135" name="__codelineno-3-135" href="#__codelineno-3-135"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">0</span>
<a id="__codelineno-3-136" name="__codelineno-3-136" href="#__codelineno-3-136"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Key and value are required&quot;</span>
<a id="__codelineno-3-137" name="__codelineno-3-137" href="#__codelineno-3-137"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-138" name="__codelineno-3-138" href="#__codelineno-3-138"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="m">1</span>
<a id="__codelineno-3-139" name="__codelineno-3-139" href="#__codelineno-3-139"></a><span class="w"> </span><span class="k">fi</span>
<a id="__codelineno-3-140" name="__codelineno-3-140" href="#__codelineno-3-140"></a>
<a id="__codelineno-3-141" name="__codelineno-3-141" href="#__codelineno-3-141"></a><span class="w"> </span><span class="c1"># Update UCI config</span>
<a id="__codelineno-3-142" name="__codelineno-3-142" href="#__codelineno-3-142"></a><span class="w"> </span>uci<span class="w"> </span><span class="nb">set</span><span class="w"> </span>&lt;module-name&gt;.general.<span class="s2">&quot;</span><span class="nv">$key</span><span class="s2">&quot;</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$value</span><span class="s2">&quot;</span>
<a id="__codelineno-3-143" name="__codelineno-3-143" href="#__codelineno-3-143"></a>
<a id="__codelineno-3-144" name="__codelineno-3-144" href="#__codelineno-3-144"></a><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$persist</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;1&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<a id="__codelineno-3-145" name="__codelineno-3-145" href="#__codelineno-3-145"></a><span class="w"> </span>uci<span class="w"> </span>commit<span class="w"> </span>&lt;module-name&gt;
<a id="__codelineno-3-146" name="__codelineno-3-146" href="#__codelineno-3-146"></a><span class="w"> </span><span class="k">fi</span>
<a id="__codelineno-3-147" name="__codelineno-3-147" href="#__codelineno-3-147"></a>
<a id="__codelineno-3-148" name="__codelineno-3-148" href="#__codelineno-3-148"></a><span class="w"> </span>json_init
<a id="__codelineno-3-149" name="__codelineno-3-149" href="#__codelineno-3-149"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">1</span>
<a id="__codelineno-3-150" name="__codelineno-3-150" href="#__codelineno-3-150"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;message&quot;</span><span class="w"> </span><span class="s2">&quot;Configuration updated&quot;</span>
<a id="__codelineno-3-151" name="__codelineno-3-151" href="#__codelineno-3-151"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-152" name="__codelineno-3-152" href="#__codelineno-3-152"></a><span class="o">}</span>
<a id="__codelineno-3-153" name="__codelineno-3-153" href="#__codelineno-3-153"></a>
<a id="__codelineno-3-154" name="__codelineno-3-154" href="#__codelineno-3-154"></a><span class="c1"># ============================================================================</span>
<a id="__codelineno-3-155" name="__codelineno-3-155" href="#__codelineno-3-155"></a><span class="c1"># Main Dispatcher</span>
<a id="__codelineno-3-156" name="__codelineno-3-156" href="#__codelineno-3-156"></a><span class="c1"># ============================================================================</span>
<a id="__codelineno-3-157" name="__codelineno-3-157" href="#__codelineno-3-157"></a>
<a id="__codelineno-3-158" name="__codelineno-3-158" href="#__codelineno-3-158"></a><span class="k">case</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="w"> </span><span class="k">in</span>
<a id="__codelineno-3-159" name="__codelineno-3-159" href="#__codelineno-3-159"></a><span class="w"> </span>list<span class="o">)</span>
<a id="__codelineno-3-160" name="__codelineno-3-160" href="#__codelineno-3-160"></a><span class="w"> </span><span class="c1"># List all available methods with their parameters</span>
<a id="__codelineno-3-161" name="__codelineno-3-161" href="#__codelineno-3-161"></a><span class="w"> </span>cat<span class="w"> </span><span class="s">&lt;&lt; &#39;EOF&#39;</span>
<a id="__codelineno-3-162" name="__codelineno-3-162" href="#__codelineno-3-162"></a><span class="s">{</span>
<a id="__codelineno-3-163" name="__codelineno-3-163" href="#__codelineno-3-163"></a><span class="s"> &quot;status&quot;: {},</span>
<a id="__codelineno-3-164" name="__codelineno-3-164" href="#__codelineno-3-164"></a><span class="s"> &quot;get_data&quot;: {},</span>
<a id="__codelineno-3-165" name="__codelineno-3-165" href="#__codelineno-3-165"></a><span class="s"> &quot;perform_action&quot;: {</span>
<a id="__codelineno-3-166" name="__codelineno-3-166" href="#__codelineno-3-166"></a><span class="s"> &quot;action_type&quot;: &quot;string&quot;,</span>
<a id="__codelineno-3-167" name="__codelineno-3-167" href="#__codelineno-3-167"></a><span class="s"> &quot;target&quot;: &quot;string&quot;</span>
<a id="__codelineno-3-168" name="__codelineno-3-168" href="#__codelineno-3-168"></a><span class="s"> },</span>
<a id="__codelineno-3-169" name="__codelineno-3-169" href="#__codelineno-3-169"></a><span class="s"> &quot;update_config&quot;: {</span>
<a id="__codelineno-3-170" name="__codelineno-3-170" href="#__codelineno-3-170"></a><span class="s"> &quot;key&quot;: &quot;string&quot;,</span>
<a id="__codelineno-3-171" name="__codelineno-3-171" href="#__codelineno-3-171"></a><span class="s"> &quot;value&quot;: &quot;string&quot;,</span>
<a id="__codelineno-3-172" name="__codelineno-3-172" href="#__codelineno-3-172"></a><span class="s"> &quot;persist&quot;: 1</span>
<a id="__codelineno-3-173" name="__codelineno-3-173" href="#__codelineno-3-173"></a><span class="s"> }</span>
<a id="__codelineno-3-174" name="__codelineno-3-174" href="#__codelineno-3-174"></a><span class="s">}</span>
<a id="__codelineno-3-175" name="__codelineno-3-175" href="#__codelineno-3-175"></a><span class="s">EOF</span>
<a id="__codelineno-3-176" name="__codelineno-3-176" href="#__codelineno-3-176"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-177" name="__codelineno-3-177" href="#__codelineno-3-177"></a><span class="w"> </span>call<span class="o">)</span>
<a id="__codelineno-3-178" name="__codelineno-3-178" href="#__codelineno-3-178"></a><span class="w"> </span><span class="c1"># Route to the appropriate method</span>
<a id="__codelineno-3-179" name="__codelineno-3-179" href="#__codelineno-3-179"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span><span class="w"> </span><span class="k">in</span>
<a id="__codelineno-3-180" name="__codelineno-3-180" href="#__codelineno-3-180"></a><span class="w"> </span>status<span class="o">)</span><span class="w"> </span>status<span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-181" name="__codelineno-3-181" href="#__codelineno-3-181"></a><span class="w"> </span>get_data<span class="o">)</span><span class="w"> </span>get_data<span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-182" name="__codelineno-3-182" href="#__codelineno-3-182"></a><span class="w"> </span>perform_action<span class="o">)</span><span class="w"> </span>perform_action<span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-183" name="__codelineno-3-183" href="#__codelineno-3-183"></a><span class="w"> </span>update_config<span class="o">)</span><span class="w"> </span>update_config<span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-184" name="__codelineno-3-184" href="#__codelineno-3-184"></a><span class="w"> </span>*<span class="o">)</span>
<a id="__codelineno-3-185" name="__codelineno-3-185" href="#__codelineno-3-185"></a><span class="w"> </span><span class="c1"># Unknown method</span>
<a id="__codelineno-3-186" name="__codelineno-3-186" href="#__codelineno-3-186"></a><span class="w"> </span>json_init
<a id="__codelineno-3-187" name="__codelineno-3-187" href="#__codelineno-3-187"></a><span class="w"> </span>json_add_boolean<span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"> </span><span class="m">0</span>
<a id="__codelineno-3-188" name="__codelineno-3-188" href="#__codelineno-3-188"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;error&quot;</span><span class="w"> </span><span class="s2">&quot;Unknown method: </span><span class="nv">$2</span><span class="s2">&quot;</span>
<a id="__codelineno-3-189" name="__codelineno-3-189" href="#__codelineno-3-189"></a><span class="w"> </span>json_dump
<a id="__codelineno-3-190" name="__codelineno-3-190" href="#__codelineno-3-190"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-191" name="__codelineno-3-191" href="#__codelineno-3-191"></a><span class="w"> </span><span class="k">esac</span>
<a id="__codelineno-3-192" name="__codelineno-3-192" href="#__codelineno-3-192"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-3-193" name="__codelineno-3-193" href="#__codelineno-3-193"></a><span class="k">esac</span>
</code></pre></div>
<p><strong>Key Points:</strong>
- Always start with <code>#!/bin/sh</code>
- Source <code>/lib/functions.sh</code> and <code>/usr/share/libubox/jshn.sh</code>
- Use <code>json_init</code>, <code>json_add_*</code>, <code>json_dump</code> for JSON output
- Read parameters from stdin using <code>read -r input</code> and <code>json_load</code>
- Always validate input parameters
- Return proper success/error status
- Implement <code>list</code> case to declare all methods and parameters
- Implement <code>call</code> case to route to method handlers</p>
<hr />
<h2 id="menu-json-template">Menu JSON Template<a class="headerlink" href="#menu-json-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>root/usr/share/luci/menu.d/luci-app-&lt;module-name&gt;.json</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="p">{</span>
<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="w"> </span><span class="nt">&quot;admin/secubox/&lt;category&gt;/&lt;module-name&gt;&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Module Title&quot;</span><span class="p">,</span>
<a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="w"> </span><span class="nt">&quot;order&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span>
<a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="w"> </span><span class="nt">&quot;action&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;firstchild&quot;</span>
<a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a><span class="w"> </span><span class="nt">&quot;depends&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a><span class="w"> </span><span class="nt">&quot;acl&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;luci-app-&lt;module-name&gt;&quot;</span><span class="p">]</span>
<a id="__codelineno-4-10" name="__codelineno-4-10" href="#__codelineno-4-10"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-4-11" name="__codelineno-4-11" href="#__codelineno-4-11"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-4-12" name="__codelineno-4-12" href="#__codelineno-4-12"></a><span class="w"> </span><span class="nt">&quot;admin/secubox/&lt;category&gt;/&lt;module-name&gt;/overview&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-13" name="__codelineno-4-13" href="#__codelineno-4-13"></a><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Overview&quot;</span><span class="p">,</span>
<a id="__codelineno-4-14" name="__codelineno-4-14" href="#__codelineno-4-14"></a><span class="w"> </span><span class="nt">&quot;order&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
<a id="__codelineno-4-15" name="__codelineno-4-15" href="#__codelineno-4-15"></a><span class="w"> </span><span class="nt">&quot;action&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-16" name="__codelineno-4-16" href="#__codelineno-4-16"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;view&quot;</span><span class="p">,</span>
<a id="__codelineno-4-17" name="__codelineno-4-17" href="#__codelineno-4-17"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;&lt;module-name&gt;/overview&quot;</span>
<a id="__codelineno-4-18" name="__codelineno-4-18" href="#__codelineno-4-18"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-4-19" name="__codelineno-4-19" href="#__codelineno-4-19"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-4-20" name="__codelineno-4-20" href="#__codelineno-4-20"></a><span class="w"> </span><span class="nt">&quot;admin/secubox/&lt;category&gt;/&lt;module-name&gt;/settings&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-21" name="__codelineno-4-21" href="#__codelineno-4-21"></a><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Settings&quot;</span><span class="p">,</span>
<a id="__codelineno-4-22" name="__codelineno-4-22" href="#__codelineno-4-22"></a><span class="w"> </span><span class="nt">&quot;order&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span>
<a id="__codelineno-4-23" name="__codelineno-4-23" href="#__codelineno-4-23"></a><span class="w"> </span><span class="nt">&quot;action&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-4-24" name="__codelineno-4-24" href="#__codelineno-4-24"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;view&quot;</span><span class="p">,</span>
<a id="__codelineno-4-25" name="__codelineno-4-25" href="#__codelineno-4-25"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;&lt;module-name&gt;/settings&quot;</span>
<a id="__codelineno-4-26" name="__codelineno-4-26" href="#__codelineno-4-26"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-4-27" name="__codelineno-4-27" href="#__codelineno-4-27"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-4-28" name="__codelineno-4-28" href="#__codelineno-4-28"></a><span class="p">}</span>
</code></pre></div>
<p><strong>Categories:</strong>
- <code>security</code> - Security &amp; monitoring modules (CrowdSec, Auth Guardian)
- <code>monitoring</code> - Monitoring modules (Netdata)
- <code>network</code> - Network modules (Netifyd, Network Modes, WireGuard)
- <code>system</code> - System modules (System Hub)
- <code>services</code> - Service modules (CDN Cache, VHost Manager)</p>
<p><strong>Key Points:</strong>
- Menu paths follow <code>admin/secubox/&lt;category&gt;/&lt;module-name&gt;</code>
- First entry uses <code>"type": "firstchild"</code> to redirect to first child
- Subsequent entries use <code>"type": "view"</code> with <code>"path"</code> matching view file location
- Path MUST match: <code>"path": "&lt;module-name&gt;/overview"</code><code>view/&lt;module-name&gt;/overview.js</code>
- Order determines menu position (lower numbers first)
- Depends on ACL entry matching package name</p>
<hr />
<h2 id="acl-json-template">ACL JSON Template<a class="headerlink" href="#acl-json-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>root/usr/share/rpcd/acl.d/luci-app-&lt;module-name&gt;.json</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="p">{</span>
<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="w"> </span><span class="nt">&quot;luci-app-&lt;module-name&gt;&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Module Title - Brief Description&quot;</span><span class="p">,</span>
<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="w"> </span><span class="nt">&quot;read&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span><span class="nt">&quot;ubus&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="w"> </span><span class="nt">&quot;luci.&lt;module-name&gt;&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a><span class="w"> </span><span class="s2">&quot;status&quot;</span><span class="p">,</span>
<a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a><span class="w"> </span><span class="s2">&quot;get_data&quot;</span><span class="p">,</span>
<a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a><span class="w"> </span><span class="s2">&quot;get_config&quot;</span><span class="p">,</span>
<a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a><span class="w"> </span><span class="s2">&quot;list_items&quot;</span>
<a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a><span class="w"> </span><span class="p">]</span>
<a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-5-13" name="__codelineno-5-13" href="#__codelineno-5-13"></a><span class="w"> </span><span class="nt">&quot;uci&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;&lt;module-name&gt;&quot;</span><span class="p">]</span>
<a id="__codelineno-5-14" name="__codelineno-5-14" href="#__codelineno-5-14"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-5-15" name="__codelineno-5-15" href="#__codelineno-5-15"></a><span class="w"> </span><span class="nt">&quot;write&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-5-16" name="__codelineno-5-16" href="#__codelineno-5-16"></a><span class="w"> </span><span class="nt">&quot;ubus&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-5-17" name="__codelineno-5-17" href="#__codelineno-5-17"></a><span class="w"> </span><span class="nt">&quot;luci.&lt;module-name&gt;&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-5-18" name="__codelineno-5-18" href="#__codelineno-5-18"></a><span class="w"> </span><span class="s2">&quot;perform_action&quot;</span><span class="p">,</span>
<a id="__codelineno-5-19" name="__codelineno-5-19" href="#__codelineno-5-19"></a><span class="w"> </span><span class="s2">&quot;update_config&quot;</span><span class="p">,</span>
<a id="__codelineno-5-20" name="__codelineno-5-20" href="#__codelineno-5-20"></a><span class="w"> </span><span class="s2">&quot;delete_item&quot;</span><span class="p">,</span>
<a id="__codelineno-5-21" name="__codelineno-5-21" href="#__codelineno-5-21"></a><span class="w"> </span><span class="s2">&quot;restart_service&quot;</span>
<a id="__codelineno-5-22" name="__codelineno-5-22" href="#__codelineno-5-22"></a><span class="w"> </span><span class="p">]</span>
<a id="__codelineno-5-23" name="__codelineno-5-23" href="#__codelineno-5-23"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-5-24" name="__codelineno-5-24" href="#__codelineno-5-24"></a><span class="w"> </span><span class="nt">&quot;uci&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;&lt;module-name&gt;&quot;</span><span class="p">]</span>
<a id="__codelineno-5-25" name="__codelineno-5-25" href="#__codelineno-5-25"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-5-26" name="__codelineno-5-26" href="#__codelineno-5-26"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-5-27" name="__codelineno-5-27" href="#__codelineno-5-27"></a><span class="p">}</span>
</code></pre></div>
<p><strong>Key Points:</strong>
- ACL entry name MUST match package name
- <code>read</code> section lists methods that can be called without write permissions
- <code>write</code> section lists methods that modify state
- ubus object names MUST match RPCD script name (<code>luci.&lt;module-name&gt;</code>)
- UCI config access can be granted separately
- All method names MUST exactly match those defined in RPCD script</p>
<hr />
<h2 id="css-styling-template">CSS Styling Template<a class="headerlink" href="#css-styling-template" title="Permanent link">&para;</a></h2>
<p><strong>File:</strong> <code>htdocs/luci-static/resources/&lt;module-name&gt;/dashboard.css</code></p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c">/**</span>
<a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="c"> * [Module Name] Dashboard Styles</span>
<a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="c"> * Extends system-hub/common.css design system</span>
<a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a><span class="c"> * Version: 1.0.0</span>
<a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="c"> */</span>
<a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a>
<a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a><span class="c">/* ============================================================================</span>
<a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="c"> IMPORTANT: Import common.css for design system variables</span>
<a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a><span class="c"> ============================================================================ */</span>
<a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a><span class="p">@</span><span class="k">import</span><span class="w"> </span><span class="nt">url</span><span class="o">(</span><span class="s1">&#39;../system-hub/common.css&#39;</span><span class="o">)</span><span class="p">;</span>
<a id="__codelineno-6-11" name="__codelineno-6-11" href="#__codelineno-6-11"></a>
<a id="__codelineno-6-12" name="__codelineno-6-12" href="#__codelineno-6-12"></a><span class="c">/* ============================================================================</span>
<a id="__codelineno-6-13" name="__codelineno-6-13" href="#__codelineno-6-13"></a><span class="c"> Module-Specific Styles</span>
<a id="__codelineno-6-14" name="__codelineno-6-14" href="#__codelineno-6-14"></a><span class="c"> ============================================================================ */</span>
<a id="__codelineno-6-15" name="__codelineno-6-15" href="#__codelineno-6-15"></a>
<a id="__codelineno-6-16" name="__codelineno-6-16" href="#__codelineno-6-16"></a><span class="c">/* Container */</span>
<a id="__codelineno-6-17" name="__codelineno-6-17" href="#__codelineno-6-17"></a><span class="p">.</span><span class="nc">module-dashboard</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-18" name="__codelineno-6-18" href="#__codelineno-6-18"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-19" name="__codelineno-6-19" href="#__codelineno-6-19"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-primary</span><span class="p">);</span>
<a id="__codelineno-6-20" name="__codelineno-6-20" href="#__codelineno-6-20"></a><span class="w"> </span><span class="k">min-height</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
<a id="__codelineno-6-21" name="__codelineno-6-21" href="#__codelineno-6-21"></a><span class="p">}</span>
<a id="__codelineno-6-22" name="__codelineno-6-22" href="#__codelineno-6-22"></a>
<a id="__codelineno-6-23" name="__codelineno-6-23" href="#__codelineno-6-23"></a><span class="c">/* Stats Grid */</span>
<a id="__codelineno-6-24" name="__codelineno-6-24" href="#__codelineno-6-24"></a><span class="p">.</span><span class="nc">stats-grid</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-25" name="__codelineno-6-25" href="#__codelineno-6-25"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="k">grid</span><span class="p">;</span>
<a id="__codelineno-6-26" name="__codelineno-6-26" href="#__codelineno-6-26"></a><span class="w"> </span><span class="k">grid-template-columns</span><span class="p">:</span><span class="w"> </span><span class="nf">repeat</span><span class="p">(</span><span class="kc">auto</span><span class="nv">-fit</span><span class="p">,</span><span class="w"> </span><span class="nf">minmax</span><span class="p">(</span><span class="mi">280</span><span class="kt">px</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="n">fr</span><span class="p">));</span>
<a id="__codelineno-6-27" name="__codelineno-6-27" href="#__codelineno-6-27"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-28" name="__codelineno-6-28" href="#__codelineno-6-28"></a><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="kt">px</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-29" name="__codelineno-6-29" href="#__codelineno-6-29"></a><span class="p">}</span>
<a id="__codelineno-6-30" name="__codelineno-6-30" href="#__codelineno-6-30"></a>
<a id="__codelineno-6-31" name="__codelineno-6-31" href="#__codelineno-6-31"></a><span class="c">/* Metric Card */</span>
<a id="__codelineno-6-32" name="__codelineno-6-32" href="#__codelineno-6-32"></a><span class="p">.</span><span class="nc">sh-metric-card</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-33" name="__codelineno-6-33" href="#__codelineno-6-33"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-card</span><span class="p">);</span>
<a id="__codelineno-6-34" name="__codelineno-6-34" href="#__codelineno-6-34"></a><span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-border</span><span class="p">);</span>
<a id="__codelineno-6-35" name="__codelineno-6-35" href="#__codelineno-6-35"></a><span class="w"> </span><span class="k">border-radius</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-36" name="__codelineno-6-36" href="#__codelineno-6-36"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-37" name="__codelineno-6-37" href="#__codelineno-6-37"></a><span class="w"> </span><span class="k">transition</span><span class="p">:</span><span class="w"> </span><span class="kc">all</span><span class="w"> </span><span class="mf">0.3</span><span class="kt">s</span><span class="w"> </span><span class="kc">ease</span><span class="p">;</span>
<a id="__codelineno-6-38" name="__codelineno-6-38" href="#__codelineno-6-38"></a><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">relative</span><span class="p">;</span>
<a id="__codelineno-6-39" name="__codelineno-6-39" href="#__codelineno-6-39"></a><span class="w"> </span><span class="k">overflow</span><span class="p">:</span><span class="w"> </span><span class="kc">hidden</span><span class="p">;</span>
<a id="__codelineno-6-40" name="__codelineno-6-40" href="#__codelineno-6-40"></a><span class="p">}</span>
<a id="__codelineno-6-41" name="__codelineno-6-41" href="#__codelineno-6-41"></a>
<a id="__codelineno-6-42" name="__codelineno-6-42" href="#__codelineno-6-42"></a><span class="p">.</span><span class="nc">sh-metric-card</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-43" name="__codelineno-6-43" href="#__codelineno-6-43"></a><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">;</span>
<a id="__codelineno-6-44" name="__codelineno-6-44" href="#__codelineno-6-44"></a><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">absolute</span><span class="p">;</span>
<a id="__codelineno-6-45" name="__codelineno-6-45" href="#__codelineno-6-45"></a><span class="w"> </span><span class="k">top</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-46" name="__codelineno-6-46" href="#__codelineno-6-46"></a><span class="w"> </span><span class="k">left</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-47" name="__codelineno-6-47" href="#__codelineno-6-47"></a><span class="w"> </span><span class="k">right</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-48" name="__codelineno-6-48" href="#__codelineno-6-48"></a><span class="w"> </span><span class="k">height</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-49" name="__codelineno-6-49" href="#__codelineno-6-49"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nb">linear-gradient</span><span class="p">(</span><span class="mi">90</span><span class="kt">deg</span><span class="p">,</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-primary</span><span class="p">),</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-primary-end</span><span class="p">));</span>
<a id="__codelineno-6-50" name="__codelineno-6-50" href="#__codelineno-6-50"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-51" name="__codelineno-6-51" href="#__codelineno-6-51"></a><span class="w"> </span><span class="k">transition</span><span class="p">:</span><span class="w"> </span><span class="k">opacity</span><span class="w"> </span><span class="mf">0.3</span><span class="kt">s</span><span class="w"> </span><span class="kc">ease</span><span class="p">;</span>
<a id="__codelineno-6-52" name="__codelineno-6-52" href="#__codelineno-6-52"></a><span class="p">}</span>
<a id="__codelineno-6-53" name="__codelineno-6-53" href="#__codelineno-6-53"></a>
<a id="__codelineno-6-54" name="__codelineno-6-54" href="#__codelineno-6-54"></a><span class="p">.</span><span class="nc">sh-metric-card</span><span class="p">:</span><span class="nd">hover</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-55" name="__codelineno-6-55" href="#__codelineno-6-55"></a><span class="w"> </span><span class="k">transform</span><span class="p">:</span><span class="w"> </span><span class="nb">translateY</span><span class="p">(</span><span class="mi">-3</span><span class="kt">px</span><span class="p">);</span>
<a id="__codelineno-6-56" name="__codelineno-6-56" href="#__codelineno-6-56"></a><span class="w"> </span><span class="k">box-shadow</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="w"> </span><span class="mi">28</span><span class="kt">px</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-hover-shadow</span><span class="p">);</span>
<a id="__codelineno-6-57" name="__codelineno-6-57" href="#__codelineno-6-57"></a><span class="p">}</span>
<a id="__codelineno-6-58" name="__codelineno-6-58" href="#__codelineno-6-58"></a>
<a id="__codelineno-6-59" name="__codelineno-6-59" href="#__codelineno-6-59"></a><span class="p">.</span><span class="nc">sh-metric-card</span><span class="p">:</span><span class="nd">hover</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-60" name="__codelineno-6-60" href="#__codelineno-6-60"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-61" name="__codelineno-6-61" href="#__codelineno-6-61"></a><span class="p">}</span>
<a id="__codelineno-6-62" name="__codelineno-6-62" href="#__codelineno-6-62"></a>
<a id="__codelineno-6-63" name="__codelineno-6-63" href="#__codelineno-6-63"></a><span class="c">/* Metric status variants */</span>
<a id="__codelineno-6-64" name="__codelineno-6-64" href="#__codelineno-6-64"></a><span class="p">.</span><span class="nc">sh-metric-ok</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-65" name="__codelineno-6-65" href="#__codelineno-6-65"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-success</span><span class="p">);</span>
<a id="__codelineno-6-66" name="__codelineno-6-66" href="#__codelineno-6-66"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-67" name="__codelineno-6-67" href="#__codelineno-6-67"></a><span class="p">}</span>
<a id="__codelineno-6-68" name="__codelineno-6-68" href="#__codelineno-6-68"></a>
<a id="__codelineno-6-69" name="__codelineno-6-69" href="#__codelineno-6-69"></a><span class="p">.</span><span class="nc">sh-metric-warning</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-70" name="__codelineno-6-70" href="#__codelineno-6-70"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-warning</span><span class="p">);</span>
<a id="__codelineno-6-71" name="__codelineno-6-71" href="#__codelineno-6-71"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-72" name="__codelineno-6-72" href="#__codelineno-6-72"></a><span class="p">}</span>
<a id="__codelineno-6-73" name="__codelineno-6-73" href="#__codelineno-6-73"></a>
<a id="__codelineno-6-74" name="__codelineno-6-74" href="#__codelineno-6-74"></a><span class="p">.</span><span class="nc">sh-metric-critical</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-75" name="__codelineno-6-75" href="#__codelineno-6-75"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-danger</span><span class="p">);</span>
<a id="__codelineno-6-76" name="__codelineno-6-76" href="#__codelineno-6-76"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-77" name="__codelineno-6-77" href="#__codelineno-6-77"></a><span class="p">}</span>
<a id="__codelineno-6-78" name="__codelineno-6-78" href="#__codelineno-6-78"></a>
<a id="__codelineno-6-79" name="__codelineno-6-79" href="#__codelineno-6-79"></a><span class="c">/* Metric Header */</span>
<a id="__codelineno-6-80" name="__codelineno-6-80" href="#__codelineno-6-80"></a><span class="p">.</span><span class="nc">sh-metric-header</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-81" name="__codelineno-6-81" href="#__codelineno-6-81"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-82" name="__codelineno-6-82" href="#__codelineno-6-82"></a><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span>
<a id="__codelineno-6-83" name="__codelineno-6-83" href="#__codelineno-6-83"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-84" name="__codelineno-6-84" href="#__codelineno-6-84"></a><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-85" name="__codelineno-6-85" href="#__codelineno-6-85"></a><span class="p">}</span>
<a id="__codelineno-6-86" name="__codelineno-6-86" href="#__codelineno-6-86"></a>
<a id="__codelineno-6-87" name="__codelineno-6-87" href="#__codelineno-6-87"></a><span class="p">.</span><span class="nc">sh-metric-icon</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-88" name="__codelineno-6-88" href="#__codelineno-6-88"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">28</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-89" name="__codelineno-6-89" href="#__codelineno-6-89"></a><span class="w"> </span><span class="k">line-height</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-90" name="__codelineno-6-90" href="#__codelineno-6-90"></a><span class="p">}</span>
<a id="__codelineno-6-91" name="__codelineno-6-91" href="#__codelineno-6-91"></a>
<a id="__codelineno-6-92" name="__codelineno-6-92" href="#__codelineno-6-92"></a><span class="p">.</span><span class="nc">sh-metric-title</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-93" name="__codelineno-6-93" href="#__codelineno-6-93"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-94" name="__codelineno-6-94" href="#__codelineno-6-94"></a><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">600</span><span class="p">;</span>
<a id="__codelineno-6-95" name="__codelineno-6-95" href="#__codelineno-6-95"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-secondary</span><span class="p">);</span>
<a id="__codelineno-6-96" name="__codelineno-6-96" href="#__codelineno-6-96"></a><span class="p">}</span>
<a id="__codelineno-6-97" name="__codelineno-6-97" href="#__codelineno-6-97"></a>
<a id="__codelineno-6-98" name="__codelineno-6-98" href="#__codelineno-6-98"></a><span class="c">/* Metric Value */</span>
<a id="__codelineno-6-99" name="__codelineno-6-99" href="#__codelineno-6-99"></a><span class="p">.</span><span class="nc">sh-metric-value</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-100" name="__codelineno-6-100" href="#__codelineno-6-100"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">40</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-101" name="__codelineno-6-101" href="#__codelineno-6-101"></a><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">700</span><span class="p">;</span>
<a id="__codelineno-6-102" name="__codelineno-6-102" href="#__codelineno-6-102"></a><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;JetBrains Mono&#39;</span><span class="p">,</span><span class="w"> </span><span class="kc">monospace</span><span class="p">;</span>
<a id="__codelineno-6-103" name="__codelineno-6-103" href="#__codelineno-6-103"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-primary</span><span class="p">);</span>
<a id="__codelineno-6-104" name="__codelineno-6-104" href="#__codelineno-6-104"></a><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-105" name="__codelineno-6-105" href="#__codelineno-6-105"></a><span class="p">}</span>
<a id="__codelineno-6-106" name="__codelineno-6-106" href="#__codelineno-6-106"></a>
<a id="__codelineno-6-107" name="__codelineno-6-107" href="#__codelineno-6-107"></a><span class="c">/* Metric Progress Bar */</span>
<a id="__codelineno-6-108" name="__codelineno-6-108" href="#__codelineno-6-108"></a><span class="p">.</span><span class="nc">sh-metric-progress</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-109" name="__codelineno-6-109" href="#__codelineno-6-109"></a><span class="w"> </span><span class="k">width</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
<a id="__codelineno-6-110" name="__codelineno-6-110" href="#__codelineno-6-110"></a><span class="w"> </span><span class="k">height</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-111" name="__codelineno-6-111" href="#__codelineno-6-111"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-tertiary</span><span class="p">);</span>
<a id="__codelineno-6-112" name="__codelineno-6-112" href="#__codelineno-6-112"></a><span class="w"> </span><span class="k">border-radius</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-113" name="__codelineno-6-113" href="#__codelineno-6-113"></a><span class="w"> </span><span class="k">overflow</span><span class="p">:</span><span class="w"> </span><span class="kc">hidden</span><span class="p">;</span>
<a id="__codelineno-6-114" name="__codelineno-6-114" href="#__codelineno-6-114"></a><span class="w"> </span><span class="k">margin-bottom</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-115" name="__codelineno-6-115" href="#__codelineno-6-115"></a><span class="p">}</span>
<a id="__codelineno-6-116" name="__codelineno-6-116" href="#__codelineno-6-116"></a>
<a id="__codelineno-6-117" name="__codelineno-6-117" href="#__codelineno-6-117"></a><span class="p">.</span><span class="nc">sh-metric-progress-bar</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-118" name="__codelineno-6-118" href="#__codelineno-6-118"></a><span class="w"> </span><span class="k">height</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
<a id="__codelineno-6-119" name="__codelineno-6-119" href="#__codelineno-6-119"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-primary</span><span class="p">);</span>
<a id="__codelineno-6-120" name="__codelineno-6-120" href="#__codelineno-6-120"></a><span class="w"> </span><span class="k">transition</span><span class="p">:</span><span class="w"> </span><span class="k">width</span><span class="w"> </span><span class="mf">0.5</span><span class="kt">s</span><span class="w"> </span><span class="kc">ease</span><span class="p">;</span>
<a id="__codelineno-6-121" name="__codelineno-6-121" href="#__codelineno-6-121"></a><span class="w"> </span><span class="k">border-radius</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-122" name="__codelineno-6-122" href="#__codelineno-6-122"></a><span class="p">}</span>
<a id="__codelineno-6-123" name="__codelineno-6-123" href="#__codelineno-6-123"></a>
<a id="__codelineno-6-124" name="__codelineno-6-124" href="#__codelineno-6-124"></a><span class="c">/* Metric Details */</span>
<a id="__codelineno-6-125" name="__codelineno-6-125" href="#__codelineno-6-125"></a><span class="p">.</span><span class="nc">sh-metric-details</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-126" name="__codelineno-6-126" href="#__codelineno-6-126"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-127" name="__codelineno-6-127" href="#__codelineno-6-127"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-secondary</span><span class="p">);</span>
<a id="__codelineno-6-128" name="__codelineno-6-128" href="#__codelineno-6-128"></a><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">500</span><span class="p">;</span>
<a id="__codelineno-6-129" name="__codelineno-6-129" href="#__codelineno-6-129"></a><span class="p">}</span>
<a id="__codelineno-6-130" name="__codelineno-6-130" href="#__codelineno-6-130"></a>
<a id="__codelineno-6-131" name="__codelineno-6-131" href="#__codelineno-6-131"></a><span class="c">/* Content Grid */</span>
<a id="__codelineno-6-132" name="__codelineno-6-132" href="#__codelineno-6-132"></a><span class="p">.</span><span class="nc">content-grid</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-133" name="__codelineno-6-133" href="#__codelineno-6-133"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="k">grid</span><span class="p">;</span>
<a id="__codelineno-6-134" name="__codelineno-6-134" href="#__codelineno-6-134"></a><span class="w"> </span><span class="k">grid-template-columns</span><span class="p">:</span><span class="w"> </span><span class="nf">repeat</span><span class="p">(</span><span class="kc">auto</span><span class="nv">-fit</span><span class="p">,</span><span class="w"> </span><span class="nf">minmax</span><span class="p">(</span><span class="mi">350</span><span class="kt">px</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="n">fr</span><span class="p">));</span>
<a id="__codelineno-6-135" name="__codelineno-6-135" href="#__codelineno-6-135"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-136" name="__codelineno-6-136" href="#__codelineno-6-136"></a><span class="w"> </span><span class="k">margin-top</span><span class="p">:</span><span class="w"> </span><span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-137" name="__codelineno-6-137" href="#__codelineno-6-137"></a><span class="p">}</span>
<a id="__codelineno-6-138" name="__codelineno-6-138" href="#__codelineno-6-138"></a>
<a id="__codelineno-6-139" name="__codelineno-6-139" href="#__codelineno-6-139"></a><span class="c">/* Component List */</span>
<a id="__codelineno-6-140" name="__codelineno-6-140" href="#__codelineno-6-140"></a><span class="p">.</span><span class="nc">component-list</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-141" name="__codelineno-6-141" href="#__codelineno-6-141"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-142" name="__codelineno-6-142" href="#__codelineno-6-142"></a><span class="w"> </span><span class="k">flex-direction</span><span class="p">:</span><span class="w"> </span><span class="kc">column</span><span class="p">;</span>
<a id="__codelineno-6-143" name="__codelineno-6-143" href="#__codelineno-6-143"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-144" name="__codelineno-6-144" href="#__codelineno-6-144"></a><span class="p">}</span>
<a id="__codelineno-6-145" name="__codelineno-6-145" href="#__codelineno-6-145"></a>
<a id="__codelineno-6-146" name="__codelineno-6-146" href="#__codelineno-6-146"></a><span class="p">.</span><span class="nc">component-item</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-147" name="__codelineno-6-147" href="#__codelineno-6-147"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-148" name="__codelineno-6-148" href="#__codelineno-6-148"></a><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span>
<a id="__codelineno-6-149" name="__codelineno-6-149" href="#__codelineno-6-149"></a><span class="w"> </span><span class="k">justify-content</span><span class="p">:</span><span class="w"> </span><span class="kc">space-between</span><span class="p">;</span>
<a id="__codelineno-6-150" name="__codelineno-6-150" href="#__codelineno-6-150"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-151" name="__codelineno-6-151" href="#__codelineno-6-151"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-secondary</span><span class="p">);</span>
<a id="__codelineno-6-152" name="__codelineno-6-152" href="#__codelineno-6-152"></a><span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-border</span><span class="p">);</span>
<a id="__codelineno-6-153" name="__codelineno-6-153" href="#__codelineno-6-153"></a><span class="w"> </span><span class="k">border-radius</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-154" name="__codelineno-6-154" href="#__codelineno-6-154"></a><span class="w"> </span><span class="k">transition</span><span class="p">:</span><span class="w"> </span><span class="kc">all</span><span class="w"> </span><span class="mf">0.2</span><span class="kt">s</span><span class="w"> </span><span class="kc">ease</span><span class="p">;</span>
<a id="__codelineno-6-155" name="__codelineno-6-155" href="#__codelineno-6-155"></a><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">relative</span><span class="p">;</span>
<a id="__codelineno-6-156" name="__codelineno-6-156" href="#__codelineno-6-156"></a><span class="w"> </span><span class="k">overflow</span><span class="p">:</span><span class="w"> </span><span class="kc">hidden</span><span class="p">;</span>
<a id="__codelineno-6-157" name="__codelineno-6-157" href="#__codelineno-6-157"></a><span class="p">}</span>
<a id="__codelineno-6-158" name="__codelineno-6-158" href="#__codelineno-6-158"></a>
<a id="__codelineno-6-159" name="__codelineno-6-159" href="#__codelineno-6-159"></a><span class="p">.</span><span class="nc">component-item</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-160" name="__codelineno-6-160" href="#__codelineno-6-160"></a><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="p">;</span>
<a id="__codelineno-6-161" name="__codelineno-6-161" href="#__codelineno-6-161"></a><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">absolute</span><span class="p">;</span>
<a id="__codelineno-6-162" name="__codelineno-6-162" href="#__codelineno-6-162"></a><span class="w"> </span><span class="k">top</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-163" name="__codelineno-6-163" href="#__codelineno-6-163"></a><span class="w"> </span><span class="k">left</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-164" name="__codelineno-6-164" href="#__codelineno-6-164"></a><span class="w"> </span><span class="k">right</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-165" name="__codelineno-6-165" href="#__codelineno-6-165"></a><span class="w"> </span><span class="k">height</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-166" name="__codelineno-6-166" href="#__codelineno-6-166"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-primary</span><span class="p">);</span>
<a id="__codelineno-6-167" name="__codelineno-6-167" href="#__codelineno-6-167"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<a id="__codelineno-6-168" name="__codelineno-6-168" href="#__codelineno-6-168"></a><span class="w"> </span><span class="k">transition</span><span class="p">:</span><span class="w"> </span><span class="k">opacity</span><span class="w"> </span><span class="mf">0.3</span><span class="kt">s</span><span class="w"> </span><span class="kc">ease</span><span class="p">;</span>
<a id="__codelineno-6-169" name="__codelineno-6-169" href="#__codelineno-6-169"></a><span class="p">}</span>
<a id="__codelineno-6-170" name="__codelineno-6-170" href="#__codelineno-6-170"></a>
<a id="__codelineno-6-171" name="__codelineno-6-171" href="#__codelineno-6-171"></a><span class="p">.</span><span class="nc">component-item</span><span class="p">:</span><span class="nd">hover</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-172" name="__codelineno-6-172" href="#__codelineno-6-172"></a><span class="w"> </span><span class="k">transform</span><span class="p">:</span><span class="w"> </span><span class="nb">translateX</span><span class="p">(</span><span class="mi">4</span><span class="kt">px</span><span class="p">);</span>
<a id="__codelineno-6-173" name="__codelineno-6-173" href="#__codelineno-6-173"></a><span class="w"> </span><span class="k">border-color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-primary</span><span class="p">);</span>
<a id="__codelineno-6-174" name="__codelineno-6-174" href="#__codelineno-6-174"></a><span class="p">}</span>
<a id="__codelineno-6-175" name="__codelineno-6-175" href="#__codelineno-6-175"></a>
<a id="__codelineno-6-176" name="__codelineno-6-176" href="#__codelineno-6-176"></a><span class="p">.</span><span class="nc">component-item</span><span class="p">:</span><span class="nd">hover</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-177" name="__codelineno-6-177" href="#__codelineno-6-177"></a><span class="w"> </span><span class="k">opacity</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-178" name="__codelineno-6-178" href="#__codelineno-6-178"></a><span class="p">}</span>
<a id="__codelineno-6-179" name="__codelineno-6-179" href="#__codelineno-6-179"></a>
<a id="__codelineno-6-180" name="__codelineno-6-180" href="#__codelineno-6-180"></a><span class="p">.</span><span class="nc">component-name</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-181" name="__codelineno-6-181" href="#__codelineno-6-181"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-182" name="__codelineno-6-182" href="#__codelineno-6-182"></a><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">600</span><span class="p">;</span>
<a id="__codelineno-6-183" name="__codelineno-6-183" href="#__codelineno-6-183"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-primary</span><span class="p">);</span>
<a id="__codelineno-6-184" name="__codelineno-6-184" href="#__codelineno-6-184"></a><span class="w"> </span><span class="k">flex</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-185" name="__codelineno-6-185" href="#__codelineno-6-185"></a><span class="p">}</span>
<a id="__codelineno-6-186" name="__codelineno-6-186" href="#__codelineno-6-186"></a>
<a id="__codelineno-6-187" name="__codelineno-6-187" href="#__codelineno-6-187"></a><span class="p">.</span><span class="nc">component-status</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-188" name="__codelineno-6-188" href="#__codelineno-6-188"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-189" name="__codelineno-6-189" href="#__codelineno-6-189"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-secondary</span><span class="p">);</span>
<a id="__codelineno-6-190" name="__codelineno-6-190" href="#__codelineno-6-190"></a><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-191" name="__codelineno-6-191" href="#__codelineno-6-191"></a><span class="p">}</span>
<a id="__codelineno-6-192" name="__codelineno-6-192" href="#__codelineno-6-192"></a>
<a id="__codelineno-6-193" name="__codelineno-6-193" href="#__codelineno-6-193"></a><span class="p">.</span><span class="nc">component-actions</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-194" name="__codelineno-6-194" href="#__codelineno-6-194"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-195" name="__codelineno-6-195" href="#__codelineno-6-195"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-196" name="__codelineno-6-196" href="#__codelineno-6-196"></a><span class="p">}</span>
<a id="__codelineno-6-197" name="__codelineno-6-197" href="#__codelineno-6-197"></a>
<a id="__codelineno-6-198" name="__codelineno-6-198" href="#__codelineno-6-198"></a><span class="c">/* Quick Actions */</span>
<a id="__codelineno-6-199" name="__codelineno-6-199" href="#__codelineno-6-199"></a><span class="p">.</span><span class="nc">quick-actions</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-200" name="__codelineno-6-200" href="#__codelineno-6-200"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-201" name="__codelineno-6-201" href="#__codelineno-6-201"></a><span class="w"> </span><span class="k">flex-wrap</span><span class="p">:</span><span class="w"> </span><span class="kc">wrap</span><span class="p">;</span>
<a id="__codelineno-6-202" name="__codelineno-6-202" href="#__codelineno-6-202"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-203" name="__codelineno-6-203" href="#__codelineno-6-203"></a><span class="p">}</span>
<a id="__codelineno-6-204" name="__codelineno-6-204" href="#__codelineno-6-204"></a>
<a id="__codelineno-6-205" name="__codelineno-6-205" href="#__codelineno-6-205"></a><span class="c">/* Activity Log */</span>
<a id="__codelineno-6-206" name="__codelineno-6-206" href="#__codelineno-6-206"></a><span class="p">.</span><span class="nc">activity-log</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-207" name="__codelineno-6-207" href="#__codelineno-6-207"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-208" name="__codelineno-6-208" href="#__codelineno-6-208"></a><span class="w"> </span><span class="k">flex-direction</span><span class="p">:</span><span class="w"> </span><span class="kc">column</span><span class="p">;</span>
<a id="__codelineno-6-209" name="__codelineno-6-209" href="#__codelineno-6-209"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-210" name="__codelineno-6-210" href="#__codelineno-6-210"></a><span class="p">}</span>
<a id="__codelineno-6-211" name="__codelineno-6-211" href="#__codelineno-6-211"></a>
<a id="__codelineno-6-212" name="__codelineno-6-212" href="#__codelineno-6-212"></a><span class="p">.</span><span class="nc">activity-item</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-213" name="__codelineno-6-213" href="#__codelineno-6-213"></a><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">flex</span><span class="p">;</span>
<a id="__codelineno-6-214" name="__codelineno-6-214" href="#__codelineno-6-214"></a><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span>
<a id="__codelineno-6-215" name="__codelineno-6-215" href="#__codelineno-6-215"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-216" name="__codelineno-6-216" href="#__codelineno-6-216"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-217" name="__codelineno-6-217" href="#__codelineno-6-217"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-secondary</span><span class="p">);</span>
<a id="__codelineno-6-218" name="__codelineno-6-218" href="#__codelineno-6-218"></a><span class="w"> </span><span class="k">border-radius</span><span class="p">:</span><span class="w"> </span><span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-219" name="__codelineno-6-219" href="#__codelineno-6-219"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-220" name="__codelineno-6-220" href="#__codelineno-6-220"></a><span class="p">}</span>
<a id="__codelineno-6-221" name="__codelineno-6-221" href="#__codelineno-6-221"></a>
<a id="__codelineno-6-222" name="__codelineno-6-222" href="#__codelineno-6-222"></a><span class="p">.</span><span class="nc">activity-time</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-223" name="__codelineno-6-223" href="#__codelineno-6-223"></a><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;JetBrains Mono&#39;</span><span class="p">,</span><span class="w"> </span><span class="kc">monospace</span><span class="p">;</span>
<a id="__codelineno-6-224" name="__codelineno-6-224" href="#__codelineno-6-224"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-secondary</span><span class="p">);</span>
<a id="__codelineno-6-225" name="__codelineno-6-225" href="#__codelineno-6-225"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-226" name="__codelineno-6-226" href="#__codelineno-6-226"></a><span class="w"> </span><span class="k">min-width</span><span class="p">:</span><span class="w"> </span><span class="mi">80</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-227" name="__codelineno-6-227" href="#__codelineno-6-227"></a><span class="p">}</span>
<a id="__codelineno-6-228" name="__codelineno-6-228" href="#__codelineno-6-228"></a>
<a id="__codelineno-6-229" name="__codelineno-6-229" href="#__codelineno-6-229"></a><span class="p">.</span><span class="nc">activity-text</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-230" name="__codelineno-6-230" href="#__codelineno-6-230"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-text-primary</span><span class="p">);</span>
<a id="__codelineno-6-231" name="__codelineno-6-231" href="#__codelineno-6-231"></a><span class="w"> </span><span class="k">flex</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<a id="__codelineno-6-232" name="__codelineno-6-232" href="#__codelineno-6-232"></a><span class="p">}</span>
<a id="__codelineno-6-233" name="__codelineno-6-233" href="#__codelineno-6-233"></a>
<a id="__codelineno-6-234" name="__codelineno-6-234" href="#__codelineno-6-234"></a><span class="c">/* ============================================================================</span>
<a id="__codelineno-6-235" name="__codelineno-6-235" href="#__codelineno-6-235"></a><span class="c"> Button Variants (Small Size)</span>
<a id="__codelineno-6-236" name="__codelineno-6-236" href="#__codelineno-6-236"></a><span class="c"> ============================================================================ */</span>
<a id="__codelineno-6-237" name="__codelineno-6-237" href="#__codelineno-6-237"></a>
<a id="__codelineno-6-238" name="__codelineno-6-238" href="#__codelineno-6-238"></a><span class="p">.</span><span class="nc">sh-btn-sm</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-239" name="__codelineno-6-239" href="#__codelineno-6-239"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">6</span><span class="kt">px</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-240" name="__codelineno-6-240" href="#__codelineno-6-240"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-241" name="__codelineno-6-241" href="#__codelineno-6-241"></a><span class="p">}</span>
<a id="__codelineno-6-242" name="__codelineno-6-242" href="#__codelineno-6-242"></a>
<a id="__codelineno-6-243" name="__codelineno-6-243" href="#__codelineno-6-243"></a><span class="c">/* ============================================================================</span>
<a id="__codelineno-6-244" name="__codelineno-6-244" href="#__codelineno-6-244"></a><span class="c"> Responsive Design</span>
<a id="__codelineno-6-245" name="__codelineno-6-245" href="#__codelineno-6-245"></a><span class="c"> ============================================================================ */</span>
<a id="__codelineno-6-246" name="__codelineno-6-246" href="#__codelineno-6-246"></a>
<a id="__codelineno-6-247" name="__codelineno-6-247" href="#__codelineno-6-247"></a><span class="p">@</span><span class="k">media</span><span class="w"> </span><span class="o">(</span><span class="nt">max-width</span><span class="o">:</span><span class="w"> </span><span class="nt">768px</span><span class="o">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-248" name="__codelineno-6-248" href="#__codelineno-6-248"></a><span class="w"> </span><span class="p">.</span><span class="nc">module-dashboard</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-249" name="__codelineno-6-249" href="#__codelineno-6-249"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-250" name="__codelineno-6-250" href="#__codelineno-6-250"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-6-251" name="__codelineno-6-251" href="#__codelineno-6-251"></a>
<a id="__codelineno-6-252" name="__codelineno-6-252" href="#__codelineno-6-252"></a><span class="w"> </span><span class="p">.</span><span class="nc">stats-grid</span><span class="o">,</span>
<a id="__codelineno-6-253" name="__codelineno-6-253" href="#__codelineno-6-253"></a><span class="w"> </span><span class="p">.</span><span class="nc">content-grid</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-254" name="__codelineno-6-254" href="#__codelineno-6-254"></a><span class="w"> </span><span class="k">grid-template-columns</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="n">fr</span><span class="p">;</span>
<a id="__codelineno-6-255" name="__codelineno-6-255" href="#__codelineno-6-255"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-6-256" name="__codelineno-6-256" href="#__codelineno-6-256"></a>
<a id="__codelineno-6-257" name="__codelineno-6-257" href="#__codelineno-6-257"></a><span class="w"> </span><span class="p">.</span><span class="nc">component-item</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-258" name="__codelineno-6-258" href="#__codelineno-6-258"></a><span class="w"> </span><span class="k">flex-direction</span><span class="p">:</span><span class="w"> </span><span class="kc">column</span><span class="p">;</span>
<a id="__codelineno-6-259" name="__codelineno-6-259" href="#__codelineno-6-259"></a><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">flex-start</span><span class="p">;</span>
<a id="__codelineno-6-260" name="__codelineno-6-260" href="#__codelineno-6-260"></a><span class="w"> </span><span class="k">gap</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-261" name="__codelineno-6-261" href="#__codelineno-6-261"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-6-262" name="__codelineno-6-262" href="#__codelineno-6-262"></a>
<a id="__codelineno-6-263" name="__codelineno-6-263" href="#__codelineno-6-263"></a><span class="w"> </span><span class="p">.</span><span class="nc">component-actions</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-264" name="__codelineno-6-264" href="#__codelineno-6-264"></a><span class="w"> </span><span class="k">width</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
<a id="__codelineno-6-265" name="__codelineno-6-265" href="#__codelineno-6-265"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-6-266" name="__codelineno-6-266" href="#__codelineno-6-266"></a>
<a id="__codelineno-6-267" name="__codelineno-6-267" href="#__codelineno-6-267"></a><span class="w"> </span><span class="p">.</span><span class="nc">sh-metric-value</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-268" name="__codelineno-6-268" href="#__codelineno-6-268"></a><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">32</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-6-269" name="__codelineno-6-269" href="#__codelineno-6-269"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-6-270" name="__codelineno-6-270" href="#__codelineno-6-270"></a><span class="p">}</span>
<a id="__codelineno-6-271" name="__codelineno-6-271" href="#__codelineno-6-271"></a>
<a id="__codelineno-6-272" name="__codelineno-6-272" href="#__codelineno-6-272"></a><span class="c">/* ============================================================================</span>
<a id="__codelineno-6-273" name="__codelineno-6-273" href="#__codelineno-6-273"></a><span class="c"> Dark Mode Enhancements</span>
<a id="__codelineno-6-274" name="__codelineno-6-274" href="#__codelineno-6-274"></a><span class="c"> ============================================================================ */</span>
<a id="__codelineno-6-275" name="__codelineno-6-275" href="#__codelineno-6-275"></a>
<a id="__codelineno-6-276" name="__codelineno-6-276" href="#__codelineno-6-276"></a><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">&quot;dark&quot;</span><span class="o">]</span><span class="w"> </span><span class="p">.</span><span class="nc">module-dashboard</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-277" name="__codelineno-6-277" href="#__codelineno-6-277"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-primary</span><span class="p">);</span>
<a id="__codelineno-6-278" name="__codelineno-6-278" href="#__codelineno-6-278"></a><span class="p">}</span>
<a id="__codelineno-6-279" name="__codelineno-6-279" href="#__codelineno-6-279"></a>
<a id="__codelineno-6-280" name="__codelineno-6-280" href="#__codelineno-6-280"></a><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">&quot;dark&quot;</span><span class="o">]</span><span class="w"> </span><span class="p">.</span><span class="nc">component-item</span><span class="o">,</span>
<a id="__codelineno-6-281" name="__codelineno-6-281" href="#__codelineno-6-281"></a><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">&quot;dark&quot;</span><span class="o">]</span><span class="w"> </span><span class="p">.</span><span class="nc">activity-item</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-282" name="__codelineno-6-282" href="#__codelineno-6-282"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-secondary</span><span class="p">);</span>
<a id="__codelineno-6-283" name="__codelineno-6-283" href="#__codelineno-6-283"></a><span class="w"> </span><span class="k">border-color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-border</span><span class="p">);</span>
<a id="__codelineno-6-284" name="__codelineno-6-284" href="#__codelineno-6-284"></a><span class="p">}</span>
<a id="__codelineno-6-285" name="__codelineno-6-285" href="#__codelineno-6-285"></a>
<a id="__codelineno-6-286" name="__codelineno-6-286" href="#__codelineno-6-286"></a><span class="o">[</span><span class="nt">data-theme</span><span class="o">=</span><span class="s2">&quot;dark&quot;</span><span class="o">]</span><span class="w"> </span><span class="p">.</span><span class="nc">component-item</span><span class="p">:</span><span class="nd">hover</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-6-287" name="__codelineno-6-287" href="#__codelineno-6-287"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--sh-bg-tertiary</span><span class="p">);</span>
<a id="__codelineno-6-288" name="__codelineno-6-288" href="#__codelineno-6-288"></a><span class="p">}</span>
</code></pre></div>
<p><strong>Key Points:</strong>
- Always import <code>system-hub/common.css</code> for design system variables
- Use CSS variables (<code>var(--sh-*)</code>) instead of hardcoded colors
- Support dark mode with <code>[data-theme="dark"]</code> selectors
- Use responsive grid layouts (<code>grid-template-columns: repeat(auto-fit, minmax(...))</code>)
- Add smooth transitions for better UX
- Use JetBrains Mono for numeric values
- Follow mobile-first responsive design</p>
<hr />
<h2 id="complete-implementation-example">Complete Implementation Example<a class="headerlink" href="#complete-implementation-example" title="Permanent link">&para;</a></h2>
<p>Here's a complete minimal working example for a new module called "Example Dashboard":</p>
<h3 id="directory-structure">Directory Structure<a class="headerlink" href="#directory-structure" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>luci-app-example-dashboard/
<a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>├── Makefile
<a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a>├── htdocs/luci-static/resources/
<a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a>│ ├── example-dashboard/
<a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a>│ │ ├── api.js
<a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a>│ │ └── dashboard.css
<a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a>│ └── view/example-dashboard/
<a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a>│ └── overview.js
<a id="__codelineno-7-9" name="__codelineno-7-9" href="#__codelineno-7-9"></a>└── root/
<a id="__codelineno-7-10" name="__codelineno-7-10" href="#__codelineno-7-10"></a> └── usr/
<a id="__codelineno-7-11" name="__codelineno-7-11" href="#__codelineno-7-11"></a> ├── libexec/rpcd/
<a id="__codelineno-7-12" name="__codelineno-7-12" href="#__codelineno-7-12"></a> │ └── luci.example-dashboard
<a id="__codelineno-7-13" name="__codelineno-7-13" href="#__codelineno-7-13"></a> └── share/
<a id="__codelineno-7-14" name="__codelineno-7-14" href="#__codelineno-7-14"></a> ├── luci/menu.d/
<a id="__codelineno-7-15" name="__codelineno-7-15" href="#__codelineno-7-15"></a> │ └── luci-app-example-dashboard.json
<a id="__codelineno-7-16" name="__codelineno-7-16" href="#__codelineno-7-16"></a> └── rpcd/acl.d/
<a id="__codelineno-7-17" name="__codelineno-7-17" href="#__codelineno-7-17"></a> └── luci-app-example-dashboard.json
</code></pre></div>
<h3 id="apijs">api.js<a class="headerlink" href="#apijs" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="s1">&#39;use strict&#39;</span><span class="p">;</span>
<a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a><span class="s1">&#39;require baseclass&#39;</span><span class="p">;</span>
<a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="s1">&#39;require rpc&#39;</span><span class="p">;</span>
<a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a>
<a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="kd">var</span><span class="w"> </span><span class="nx">callStatus</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">rpc</span><span class="p">.</span><span class="nx">declare</span><span class="p">({</span>
<a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="w"> </span><span class="nx">object</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;luci.example-dashboard&#39;</span><span class="p">,</span>
<a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="w"> </span><span class="nx">method</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;status&#39;</span><span class="p">,</span>
<a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="w"> </span><span class="nx">expect</span><span class="o">:</span><span class="w"> </span><span class="p">{}</span>
<a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a><span class="p">});</span>
<a id="__codelineno-8-10" name="__codelineno-8-10" href="#__codelineno-8-10"></a>
<a id="__codelineno-8-11" name="__codelineno-8-11" href="#__codelineno-8-11"></a><span class="k">return</span><span class="w"> </span><span class="nx">baseclass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<a id="__codelineno-8-12" name="__codelineno-8-12" href="#__codelineno-8-12"></a><span class="w"> </span><span class="nx">getStatus</span><span class="o">:</span><span class="w"> </span><span class="nx">callStatus</span>
<a id="__codelineno-8-13" name="__codelineno-8-13" href="#__codelineno-8-13"></a><span class="p">});</span>
</code></pre></div>
<h3 id="overviewjs">overview.js<a class="headerlink" href="#overviewjs" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="s1">&#39;use strict&#39;</span><span class="p">;</span>
<a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="s1">&#39;require view&#39;</span><span class="p">;</span>
<a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="s1">&#39;require example-dashboard/api as API&#39;</span><span class="p">;</span>
<a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a>
<a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="k">return</span><span class="w"> </span><span class="nx">view</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a><span class="w"> </span><span class="nx">load</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">API</span><span class="p">.</span><span class="nx">getStatus</span><span class="p">();</span>
<a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a>
<a id="__codelineno-9-10" name="__codelineno-9-10" href="#__codelineno-9-10"></a><span class="w"> </span><span class="nx">render</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-9-11" name="__codelineno-9-11" href="#__codelineno-9-11"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="p">[</span>
<a id="__codelineno-9-12" name="__codelineno-9-12" href="#__codelineno-9-12"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;link&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">&#39;rel&#39;</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;stylesheet&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;href&#39;</span><span class="o">:</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">resource</span><span class="p">(</span><span class="s1">&#39;example-dashboard/dashboard.css&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">}),</span>
<a id="__codelineno-9-13" name="__codelineno-9-13" href="#__codelineno-9-13"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;h2&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="s1">&#39;Example Dashboard&#39;</span><span class="p">),</span>
<a id="__codelineno-9-14" name="__codelineno-9-14" href="#__codelineno-9-14"></a><span class="w"> </span><span class="nx">E</span><span class="p">(</span><span class="s1">&#39;p&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="s1">&#39;Status: &#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">status</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">&#39;Unknown&#39;</span><span class="p">))</span>
<a id="__codelineno-9-15" name="__codelineno-9-15" href="#__codelineno-9-15"></a><span class="w"> </span><span class="p">]);</span>
<a id="__codelineno-9-16" name="__codelineno-9-16" href="#__codelineno-9-16"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-9-17" name="__codelineno-9-17" href="#__codelineno-9-17"></a>
<a id="__codelineno-9-18" name="__codelineno-9-18" href="#__codelineno-9-18"></a><span class="w"> </span><span class="nx">handleSaveApply</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-9-19" name="__codelineno-9-19" href="#__codelineno-9-19"></a><span class="w"> </span><span class="nx">handleSave</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
<a id="__codelineno-9-20" name="__codelineno-9-20" href="#__codelineno-9-20"></a><span class="w"> </span><span class="nx">handleReset</span><span class="o">:</span><span class="w"> </span><span class="kc">null</span>
<a id="__codelineno-9-21" name="__codelineno-9-21" href="#__codelineno-9-21"></a><span class="p">});</span>
</code></pre></div>
<h3 id="luciexample-dashboard-rpcd">luci.example-dashboard (RPCD)<a class="headerlink" href="#luciexample-dashboard-rpcd" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="ch">#!/bin/sh</span>
<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>.<span class="w"> </span>/usr/share/libubox/jshn.sh
<a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a>
<a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>status<span class="o">()</span><span class="w"> </span><span class="o">{</span>
<a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="w"> </span>json_init
<a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;status&quot;</span><span class="w"> </span><span class="s2">&quot;running&quot;</span>
<a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="w"> </span>json_add_string<span class="w"> </span><span class="s2">&quot;version&quot;</span><span class="w"> </span><span class="s2">&quot;1.0.0&quot;</span>
<a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a><span class="w"> </span>json_dump
<a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="o">}</span>
<a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a>
<a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="k">case</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="w"> </span><span class="k">in</span>
<a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a><span class="w"> </span>list<span class="o">)</span>
<a id="__codelineno-10-13" name="__codelineno-10-13" href="#__codelineno-10-13"></a><span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s1">&#39;{&quot;status&quot;:{}}&#39;</span>
<a id="__codelineno-10-14" name="__codelineno-10-14" href="#__codelineno-10-14"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-10-15" name="__codelineno-10-15" href="#__codelineno-10-15"></a><span class="w"> </span>call<span class="o">)</span>
<a id="__codelineno-10-16" name="__codelineno-10-16" href="#__codelineno-10-16"></a><span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span><span class="w"> </span><span class="k">in</span>
<a id="__codelineno-10-17" name="__codelineno-10-17" href="#__codelineno-10-17"></a><span class="w"> </span>status<span class="o">)</span><span class="w"> </span>status<span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-10-18" name="__codelineno-10-18" href="#__codelineno-10-18"></a><span class="w"> </span><span class="k">esac</span>
<a id="__codelineno-10-19" name="__codelineno-10-19" href="#__codelineno-10-19"></a><span class="w"> </span><span class="p">;;</span>
<a id="__codelineno-10-20" name="__codelineno-10-20" href="#__codelineno-10-20"></a><span class="k">esac</span>
</code></pre></div>
<h3 id="menudluci-app-example-dashboardjson">menu.d/luci-app-example-dashboard.json<a class="headerlink" href="#menudluci-app-example-dashboardjson" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="p">{</span>
<a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="nt">&quot;admin/secubox/monitoring/example-dashboard&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Example Dashboard&quot;</span><span class="p">,</span>
<a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="w"> </span><span class="nt">&quot;order&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span>
<a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="w"> </span><span class="nt">&quot;action&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;firstchild&quot;</span>
<a id="__codelineno-11-7" name="__codelineno-11-7" href="#__codelineno-11-7"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-11-8" name="__codelineno-11-8" href="#__codelineno-11-8"></a><span class="w"> </span><span class="nt">&quot;depends&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-11-9" name="__codelineno-11-9" href="#__codelineno-11-9"></a><span class="w"> </span><span class="nt">&quot;acl&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;luci-app-example-dashboard&quot;</span><span class="p">]</span>
<a id="__codelineno-11-10" name="__codelineno-11-10" href="#__codelineno-11-10"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-11-11" name="__codelineno-11-11" href="#__codelineno-11-11"></a><span class="w"> </span><span class="p">},</span>
<a id="__codelineno-11-12" name="__codelineno-11-12" href="#__codelineno-11-12"></a><span class="w"> </span><span class="nt">&quot;admin/secubox/monitoring/example-dashboard/overview&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-11-13" name="__codelineno-11-13" href="#__codelineno-11-13"></a><span class="w"> </span><span class="nt">&quot;title&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Overview&quot;</span><span class="p">,</span>
<a id="__codelineno-11-14" name="__codelineno-11-14" href="#__codelineno-11-14"></a><span class="w"> </span><span class="nt">&quot;order&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
<a id="__codelineno-11-15" name="__codelineno-11-15" href="#__codelineno-11-15"></a><span class="w"> </span><span class="nt">&quot;action&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-11-16" name="__codelineno-11-16" href="#__codelineno-11-16"></a><span class="w"> </span><span class="nt">&quot;type&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;view&quot;</span><span class="p">,</span>
<a id="__codelineno-11-17" name="__codelineno-11-17" href="#__codelineno-11-17"></a><span class="w"> </span><span class="nt">&quot;path&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;example-dashboard/overview&quot;</span>
<a id="__codelineno-11-18" name="__codelineno-11-18" href="#__codelineno-11-18"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-11-19" name="__codelineno-11-19" href="#__codelineno-11-19"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-11-20" name="__codelineno-11-20" href="#__codelineno-11-20"></a><span class="p">}</span>
</code></pre></div>
<h3 id="acldluci-app-example-dashboardjson">acl.d/luci-app-example-dashboard.json<a class="headerlink" href="#acldluci-app-example-dashboardjson" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="p">{</span>
<a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span><span class="nt">&quot;luci-app-example-dashboard&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="nt">&quot;description&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Example Dashboard&quot;</span><span class="p">,</span>
<a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="w"> </span><span class="nt">&quot;read&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a><span class="w"> </span><span class="nt">&quot;ubus&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="w"> </span><span class="nt">&quot;luci.example-dashboard&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;status&quot;</span><span class="p">]</span>
<a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-12-9" name="__codelineno-12-9" href="#__codelineno-12-9"></a><span class="w"> </span><span class="p">}</span>
<a id="__codelineno-12-10" name="__codelineno-12-10" href="#__codelineno-12-10"></a><span class="p">}</span>
</code></pre></div>
<h3 id="dashboardcss">dashboard.css<a class="headerlink" href="#dashboardcss" title="Permanent link">&para;</a></h3>
<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="p">@</span><span class="k">import</span><span class="w"> </span><span class="nt">url</span><span class="o">(</span><span class="s1">&#39;../system-hub/common.css&#39;</span><span class="o">)</span><span class="p">;</span>
<a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>
<a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="nt">div</span><span class="w"> </span><span class="p">{</span>
<a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
<a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="p">}</span>
</code></pre></div>
<p><strong>Installation Steps:</strong>
1. Copy files to module directory
2. Set RPCD permissions: <code>chmod +x root/usr/libexec/rpcd/luci.example-dashboard</code>
3. Validate: <code>./secubox-tools/validate-modules.sh</code>
4. Build: <code>./secubox-tools/local-build.sh build luci-app-example-dashboard</code>
5. Deploy: <code>scp build/x86-64/*.ipk root@router:/tmp/</code>
6. Install: <code>ssh root@router "opkg install /tmp/luci-app-example-dashboard*.ipk &amp;&amp; /etc/init.d/rpcd restart"</code></p>
<hr />
<h2 id="common-pitfalls-and-solutions">Common Pitfalls and Solutions<a class="headerlink" href="#common-pitfalls-and-solutions" title="Permanent link">&para;</a></h2>
<h3 id="1-rpcd-object-not-found-error">1. RPCD "Object not found" Error<a class="headerlink" href="#1-rpcd-object-not-found-error" title="Permanent link">&para;</a></h3>
<p><strong>Error:</strong> <code>RPC call to luci.example-dashboard/status failed with error -32000: Object not found</code></p>
<p><strong>Solutions:</strong>
- Check RPCD script name matches ubus object name exactly (including <code>luci.</code> prefix)
- Verify RPCD script is executable: <code>chmod +x root/usr/libexec/rpcd/luci.example-dashboard</code>
- Restart RPCD service: <code>/etc/init.d/rpcd restart</code>
- Check RPCD logs: <code>logread | grep rpcd</code></p>
<h3 id="2-http-404-view-not-found">2. HTTP 404 View Not Found<a class="headerlink" href="#2-http-404-view-not-found" title="Permanent link">&para;</a></h3>
<p><strong>Error:</strong> <code>HTTP error 404 while loading class file '/luci-static/resources/view/example-dashboard/overview.js'</code></p>
<p><strong>Solutions:</strong>
- Verify menu path matches view file location exactly
- Menu: <code>"path": "example-dashboard/overview"</code> → File: <code>view/example-dashboard/overview.js</code>
- Check file permissions: <code>644</code> for CSS/JS files
- Clear browser cache</p>
<h3 id="3-acl-permission-denied">3. ACL Permission Denied<a class="headerlink" href="#3-acl-permission-denied" title="Permanent link">&para;</a></h3>
<p><strong>Error:</strong> Access denied or missing permissions</p>
<p><strong>Solutions:</strong>
- Verify ACL file exists in <code>root/usr/share/rpcd/acl.d/</code>
- Check ubus object name in ACL matches RPCD script name
- Restart RPCD: <code>/etc/init.d/rpcd restart</code>
- Check method is listed in appropriate section (read vs write)</p>
<h3 id="4-css-not-loading">4. CSS Not Loading<a class="headerlink" href="#4-css-not-loading" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Styles not applied</p>
<p><strong>Solutions:</strong>
- Verify CSS import path: <code>L.resource('example-dashboard/dashboard.css')</code>
- Check file permissions: <code>644</code>
- Import common.css: <code>@import url('../system-hub/common.css');</code>
- Clear browser cache
- Check browser console for 404 errors</p>
<h3 id="5-auto-refresh-not-working">5. Auto-Refresh Not Working<a class="headerlink" href="#5-auto-refresh-not-working" title="Permanent link">&para;</a></h3>
<p><strong>Problem:</strong> Poll not updating dashboard</p>
<p><strong>Solutions:</strong>
- Verify poll.add() is called in render() method
- Check API calls in poll callback return Promises
- Ensure updateDashboard() method updates DOM correctly
- Use browser console to check for JavaScript errors</p>
<hr />
<h2 id="validation-checklist">Validation Checklist<a class="headerlink" href="#validation-checklist" title="Permanent link">&para;</a></h2>
<p>Before deploying, always run these checks:</p>
<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="c1"># 1. Fix permissions</span>
<a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>./secubox-tools/fix-permissions.sh<span class="w"> </span>--local
<a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a>
<a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="c1"># 2. Validate module structure</span>
<a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a>./secubox-tools/validate-modules.sh
<a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a>
<a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="c1"># 3. Validate JSON syntax</span>
<a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a>find<span class="w"> </span>luci-app-example-dashboard<span class="w"> </span>-name<span class="w"> </span><span class="s2">&quot;*.json&quot;</span><span class="w"> </span>-exec<span class="w"> </span>jsonlint<span class="w"> </span><span class="o">{}</span><span class="w"> </span><span class="se">\;</span>
<a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a>
<a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="c1"># 4. Validate shell scripts</span>
<a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a>shellcheck<span class="w"> </span>luci-app-example-dashboard/root/usr/libexec/rpcd/*
<a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a>
<a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="c1"># 5. Build locally</span>
<a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></a>./secubox-tools/local-build.sh<span class="w"> </span>build<span class="w"> </span>luci-app-example-dashboard
</code></pre></div>
<hr />
<p><strong>Document Version:</strong> 1.0.0
<strong>Last Updated:</strong> 2025-12-27
<strong>Maintainer:</strong> CyberMind.fr</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
<div class="md-social">
<a href="https://github.com/CyberMind-FR/secubox-openwrt" target="_blank" rel="noopener" title="github.com" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://secubox.cybermood.eu" target="_blank" rel="noopener" title="secubox.cybermood.eu" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M351.9 280H161c2.9 64.5 17.2 123.9 37.5 167.4 11.4 24.5 23.7 41.8 35.1 52.4 11.2 10.5 18.9 12.2 22.9 12.2s11.7-1.7 22.9-12.2c11.4-10.6 23.7-28 35.1-52.4 20.3-43.5 34.6-102.9 37.5-167.4zm-191-48h190.9c-2.8-64.5-17.1-123.9-37.4-167.4-11.4-24.4-23.7-41.8-35.1-52.4C268.1 1.7 260.4 0 256.4 0s-11.7 1.7-22.9 12.2c-11.4 10.6-23.7 28-35.1 52.4-20.3 43.5-34.6 102.9-37.5 167.4m-48 0c3.5-85.6 25.6-165.1 57.9-217.3C78.7 47.3 10.9 131.2 1.5 232zM1.5 280c9.4 100.8 77.2 184.7 169.3 217.3-32.3-52.2-54.4-131.7-57.9-217.3zm398.4 0c-3.5 85.6-25.6 165.1-57.9 217.3 92.1-32.7 159.9-116.5 169.3-217.3zm111.4-48C501.9 131.2 434.1 47.3 342 14.7c32.3 52.2 54.4 131.7 57.9 217.3z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "..", "features": ["navigation.instant", "navigation.tracking", "navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.expand", "navigation.top", "search.suggest", "search.highlight", "content.code.copy", "content.code.annotate"], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script>
<script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
</body>
</html>