rustploy-gui 0.1.28

Rustploy — desktop client (glacier-ui) for the Rustploy PaaS daemon
// 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/running_card.gvb", as = RunningCard)
    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 = "Em execução",
        icon = "●",
        target = running,
        action = nav_running,
      )

      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 = Runtime, 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,
        badge = @update_is_available,
      )

      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
      }
    }
  }
}