rustploy-gui 0.1.3

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(): ()
    -- `np_name`/`np_desc` 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.np_name = ctx.np_name or ""
    ctx.np_desc = ctx.np_desc or ""
    ctx.np_err = ""
    ctx.np_msg = ""
    ctx.np_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 np_apontar(_erros_json: string?): ()
    ctx.np_msg = ""
    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 `np_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.np_err = "sem conexão — reabra a janela pelo app"
        return
    end
    ctx.np_err = ""
    local name = Helpers.trim(ctx.np_name)
    local desc = Helpers.trim(ctx.np_desc)
    ctx.np_msg = "criando…"
    ctx.np_busy = "true"

    local ok, res = api:rpc_checked{
        ProjectCreate = {
            name = name,
            description = (desc ~= "" and desc or nil),
        },
    }
    ctx.np_busy = ""
    if not ok then
        ctx.np_msg = ""
        ctx.np_err = "erro: " .. tostring(res)
        return
    end

    -- Sucesso: envia os dados do projeto criado à janela inicial e fecha esta.
    local p = (type(res) == "table" and res.Project) or {}
    broadcast("project_created", {
        id = p.id,
        name = p.name or name,
        description = p.description or desc,
    })
    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