rustploy-gui 0.1.9

Rustploy — desktop client (glacier-ui) for the Rustploy PaaS daemon
--!strict
--!nolint FunctionUnused
-- new_project_window.luau — script da JANELA de "Novo projeto", um motor Glacier
-- próprio e ISOLADO do app principal (aberto via open_window). Recebe a conexão
-- (api_url/api_token) semeada no contexto por open_window({ data = ... }); ao
-- criar o projeto emite broadcast("project_created") para a janela principal
-- (recebido por on_broadcast em handlers/projects.luau) e fecha a si mesma.
--
-- require resolve relativo a ESTE arquivo (scripts/): "net/api" e "helpers".

local Api = require("net/api")
local Helpers = require("helpers")

-- Reconstrói o cliente da API a partir da conexão semeada no contexto.
local function client(): Api.Client?
    local url = ctx.api_url or ""
    if url == "" then
        return nil
    end
    return Api.new(url, ctx.api_token)
end

function init(): ()
    -- `new_project_name`/`new_project_description` são as chaves que os
    -- `form_control` do form(...) ligam
    -- (sem `value=`/`on_change=`: o motor grava sozinho). Só o valor inicial é
    -- semeado aqui; os `erro_np_*` são publicados pelo motor na validação.
    ctx.new_project_name = ctx.new_project_name or ""
    ctx.new_project_description = ctx.new_project_description or ""
    ctx.new_project_error = ""
    ctx.new_project_message = ""
    ctx.new_project_busy = ""
end

-- on_validation_error do form(...): as falhas já vêm prontas em JSON
-- (`[{campo,msg}]`) e os `{erro_<campo>}` + o `:invalid` já estão na tela —
-- aqui é só o aviso.
function new_project_show_validation_errors(_erros_json: string?): ()
    ctx.new_project_message = ""
    toast{ message = "Corrija os campos destacados", kind = "warning" }
end

-- on_submit do form(...): só roda quando a validação (rules="required" no campo
-- NOME) passou, então `new_project_name` está garantidamente preenchido. `fetch` (dentro
-- de rpc_checked) SUSPENDE a corrotina; ao voltar, em sucesso avisa a janela
-- principal e se fecha.
function submit_project(): ()
    local api = client()
    if not api then
        ctx.new_project_error = "sem conexão — reabra a janela pelo app"
        return
    end
    ctx.new_project_error = ""
    local name = Helpers.trim(ctx.new_project_name)
    local description = Helpers.trim(ctx.new_project_description)
    ctx.new_project_message = "criando…"
    ctx.new_project_busy = "true"

    local project_create_ok, project_create_response = api:rpc_checked{
        ProjectCreate = {
            name = name,
            description = (description ~= "" and description or nil),
        },
    }
    ctx.new_project_busy = ""
    if not project_create_ok then
        ctx.new_project_message = ""
        ctx.new_project_error = "erro: " .. tostring(project_create_response)
        return
    end

    -- Sucesso: envia os dados do projeto criado à janela inicial e fecha esta.
    local project = (type(project_create_response) == "table" and project_create_response.Project) or {}
    broadcast("project_created", {
        id = project.id,
        name = project.name or name,
        description = project.description or description,
    })
    close_window()
end

-- Botão "Cancelar": fecha ESTA janela (close_window fecha a janela dona deste
-- motor com precisão, sem depender de qual janela está em foco).
function cancel(): ()
    close_window()
end

-- Trava de ação em andamento (ver busy.luau): por último, com todas as funções já definidas.
require("busy").install(require("busy_actions"))