// Casca do app conectado: sidebar, topbar e as views de projeto (Deployments,
// Projects, serviços de um projeto, detalhe de serviço); importa home.gv para
// as telas globais.
component {
resources {
// ── Imports ────────────────────────────────────────────────────
link(rel = import, href = "service.gvb", as = ServiceDetail)
link(rel = import, href = "home.gvb", as = HomeViews)
link(rel = import, href = "shell/deployments.gvb", as = DeploymentsView)
link(rel = import, href = "shell/projects.gvb", as = ProjectsView)
link(rel = import, href = "shell/project_services.gvb", as = ProjectServicesView)
link(rel = import, href = "components/project_card.gvb", as = ProjectCard)
link(rel = import, href = "components/service_card.gvb", as = ServiceCard)
link(rel = import, href = "components/stat_card.gvb", as = StatCard)
link(rel = import, href = "components/state_cell.gvb", as = StateCell)
link(rel = import, href = "components/job_actions.gvb", as = JobActions)
link(rel = import, href = "components/badge.gvb", as = rp-badge)
link(rel = import, href = "components/nav_item.gvb", as = NavItem)
link(rel = import, href = "components/tab_button.gvb", as = TabButton)
link(rel = import, href = "components/loading_row.gvb", as = LoadingRow)
link(rel = import, href = "components/picker_row.gvb", as = PickerRow)
link(rel = import, href = "components/template_row.gvb", as = TemplateRow)
style """
.sidebar { background: var(--surface); }
.brand { spacing: 2; padding: 0 8 18 8; }
.brand__name { size: 21; bold: true; color: var(--text); }
.brand__sub { size: 11; color: var(--faint); }
.sidebar__section { size: 11; bold: true; color: var(--faint); padding: 16 8 6 8; }
.sidebar__spacer { height: fill; }
.shell__main { width: fill; height: fill; }
/* Top bar */
.topbar { width: fill; padding: 18 28; spacing: 18; align-y: center; }
.topbar__menu { color: var(--border); text-color: var(--text); padding: 9 13; border-radius: 5; }
.topbar__search { width: fill; padding: 11 14; }
.topbar__actions { spacing: 8; align-y: center; }
/* Ações do cabeçalho do projeto (variantes compactas, à direita) */
.proj-bar { width: fill; align-y: center; }
.proj-bar__actions { spacing: 8; align-y: center; }
@media (max-width: 1100) {
.topbar__status { hidden: true; }
}
@media (max-width: 900) {
.topbar { padding: 12 16; spacing: 10; }
}
/* Com a sidebar aberta sobram ~350px de conteúdo aqui: a busca some antes
de espremer os botões de ação (ficava "Search d"). */
@media (max-width: 760) {
.topbar__search { hidden: true; }
}
@media (max-width: 560) {
.topbar__search { hidden: true; }
.topbar__status { hidden: true; }
}
"""
}
row(class = screen) {
// ── Sidebar (gaveta) ─────────────────────────────────────────────────
drawer(
:value = menu,
open = @menu,
size = 264,
padding = "28 20",
spacing = 6,
panel_class = sidebar,
) {
column(class = brand) {
text(class = brand__name, content = "Rustploy")
text(class = brand__sub, content = "PRODUCTION CLUSTER")
}
text(class = sidebar__section, content = "DEPLOYMENT OPS")
NavItem(
label = Deployments,
icon = "▶",
target = deployments,
action = nav_deployments,
)
NavItem(
label = "Deploy Engine",
icon = "⚡",
target = deploy_engine,
action = nav_deploy_engine,
)
NavItem(
label = Monitoring,
icon = "◔",
target = monitoring,
action = nav_monitoring,
)
NavItem(
label = Schedules,
icon = "◫",
target = schedules,
action = nav_schedules,
)
NavItem(
label = Ingress,
icon = "↦",
target = ingress,
action = nav_ingress,
)
NavItem(label = Docker, icon = "◉", target = docker, action = nav_docker)
text(class = sidebar__section, content = "MANAGEMENT")
NavItem(
label = "Projects (@projects_count)",
icon = "◧",
target = "projects project_services service",
action = nav_projects,
)
column(class = sidebar__spacer)
hr
NavItem(
label = Settings,
icon = "⌥",
target = settings,
action = nav_settings,
)
NavItem(
label = Support,
icon = "◊",
target = support,
action = nav_support,
)
}
// ── Main column ────────────────────────────────────────────────────
column(class = shell__main) {
// Top bar
row(class = topbar) {
button(
class = topbar__menu,
text = "☰",
on_click = app:drawer::toggle:menu,
tooltip = "Mostrar/ocultar o menu",
)
input(
class = topbar__search,
:value = search,
on_change = search_changed,
placeholder = "Search deployments, nodes…",
)
text(
class = "u-muted topbar__status",
content = "● @connected_server · daemon v@daemon_version",
)
flow(class = topbar__actions) {
button(class = "btn btn--danger", on_click = stop_all, disabled = @busy_stop_all, text = "Stop All")
button(class = "btn btn--ghost", on_click = disconnect, disabled = @busy_disconnect, text = Disconnect)
}
}
hr
// Content
if @view == "deployments" {
DeploymentsView
} else if @view == "projects" {
ProjectsView
} else if @view == "project_services" {
ProjectServicesView
} else if @view == "service" {
// ── Service detail / demais views ────────────────────────────
ServiceDetail
} else {
HomeViews
}
}
}
}