Compare commits

...

2 commits

Author SHA1 Message Date
686b2f62f3
web: start: menu css
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
2026-06-02 15:04:48 +02:00
3323da955d
web: start: serparator for menu
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
2026-06-02 15:03:39 +02:00
5 changed files with 58 additions and 18 deletions

View file

@ -242,29 +242,35 @@ for spec in "${REMOTE_PAGES[@]}"; do
done done
if [[ "$CHECK_ONLY" == false ]]; then if [[ "$CHECK_ONLY" == false ]]; then
# Apply menu order if defined — reorder PAGE_ENTRIES and filter nav visibility # Build navigation.json from menu: section.
mapfile -t MENU_ORDER < <(yq '(.menu // [])[]' "$MANIFEST") # Convert full manifest to JSON once (single yq call) then jq handles all logic.
# Menu items: string → page lookup, object → pass-through (separator/section).
manifest_json=$(yq -o json '.' "$MANIFEST")
menu_json=$(echo "$manifest_json" | jq -c '.menu // []')
menu_len=$(echo "$menu_json" | jq 'length')
if [[ ${#MENU_ORDER[@]} -gt 0 ]]; then if [[ "$menu_len" -gt 0 ]]; then
ORDERED="[]" NAV_ENTRIES=$(jq -n \
for route in "${MENU_ORDER[@]}"; do --argjson menu "$menu_json" \
[[ -z "$route" ]] && continue --argjson pages "$PAGE_ENTRIES" \
entry="$(echo "$PAGE_ENTRIES" | jq --arg r "$route" '.[] | select(.route == $r)')" '[ $menu[] |
[[ -z "$entry" ]] && warn "menu: route '${route}' not found in pages, skipping" if type == "string"
[[ -n "$entry" ]] && ORDERED="$(echo "$ORDERED" | jq --argjson e "$entry" '. + [$e]')" then (. as $r | $pages[] | select(.route == $r) |
done { label, href: ("#/" + .route), icon })
NAV_ENTRIES="$ORDERED" else .
end
]')
else else
NAV_ENTRIES="$PAGE_ENTRIES" # No menu defined — use all pages in discovery order
NAV_ENTRIES=$(echo "$PAGE_ENTRIES" | \
jq '[.[] | { label, href: ("#/" + .route), icon }]')
fi fi
# pages.json — full list (all pages, original discovery order) # pages.json — full list (all pages, original discovery order)
echo "$PAGE_ENTRIES" | jq '.' > "${WEB_DIR}/pages.json" echo "$PAGE_ENTRIES" | jq '.' > "${WEB_DIR}/pages.json"
# navigation.json — ordered + filtered by menu: # navigation.json — write NAV_ENTRIES as-is (already correctly shaped)
echo "$NAV_ENTRIES" | \ echo "$NAV_ENTRIES" | jq '.' > "${WEB_DIR}/navigation.json"
jq '[.[] | {label: .label, href: ("#/" + .route), icon: .icon}]' \
> "${WEB_DIR}/navigation.json"
log "wrote pages.json ($(echo "$PAGE_ENTRIES" | jq 'length') pages) + navigation.json ($(echo "$NAV_ENTRIES" | jq 'length') in menu)" log "wrote pages.json ($(echo "$PAGE_ENTRIES" | jq 'length') pages) + navigation.json ($(echo "$NAV_ENTRIES" | jq 'length') in menu)"
fi fi

View file

@ -52,7 +52,10 @@ remote_pages: []
# A page not listed here is accessible via /#/route but hidden from the nav. # A page not listed here is accessible via /#/route but hidden from the nav.
menu: menu:
- dashboard - dashboard
- type: section
label: Réseau
- vpcs - vpcs
- subnets - subnets
- type: separator
- vms - vms

View file

@ -16,13 +16,14 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 11px 12px; padding: 5px 12px;
margin: 2px;
border-radius: 6px; border-radius: 6px;
text-decoration: none; text-decoration: none;
color: #a6adc8; color: #a6adc8;
font-size: 14px; font-size: 14px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-height: 44px; min-height: 25px;
transition: background 0.1s, color 0.1s; transition: background 0.1s, color 0.1s;
} }
@ -38,3 +39,18 @@
} }
.item:hover .icon { color: #cdd6f4; } .item:hover .icon { color: #cdd6f4; }
.section-header {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #45475a;
padding: 14px 12px 4px;
}
.separator {
height: 1px;
background: #313244;
margin: 6px 4px;
}

View file

@ -76,6 +76,14 @@ class SideNav extends HTMLElement {
const current = this.#currentHref() const current = this.#currentHref()
const links = this.#items.map(item => { const links = this.#items.map(item => {
if (item.type === 'separator') {
return `<div class="separator"></div>`
}
if (item.type === 'section') {
return `<div class="section-header">${item.label ?? ''}</div>`
}
// Skip malformed entries (missing href or label)
if (!item.href || !item.label) return ''
const active = item.href === current const active = item.href === current
return `<a href="${item.href}" class="item ${active ? 'active' : ''}"> return `<a href="${item.href}" class="item ${active ? 'active' : ''}">
<span class="icon">${resolveIcon(item.icon)}</span> <span class="icon">${resolveIcon(item.icon)}</span>

View file

@ -4,6 +4,10 @@
"href": "#/dashboard", "href": "#/dashboard",
"icon": "dashboard" "icon": "dashboard"
}, },
{
"type": "section",
"label": "Réseau"
},
{ {
"label": "VPCs", "label": "VPCs",
"href": "#/vpcs", "href": "#/vpcs",
@ -14,6 +18,9 @@
"href": "#/subnets", "href": "#/subnets",
"icon": "subnet" "icon": "subnet"
}, },
{
"type": "separator"
},
{ {
"label": "VMs", "label": "VMs",
"href": "#/vms", "href": "#/vms",