Pad env rows and reflow the envs group

Give each env row its own horizontal padding so the key lines up with the
group's other content, and reflow the props and description literals in
env-row and envs-group.
This commit is contained in:
2026-10-22 15:24:58 +01:00
parent e1faa2141e
commit ceae14dc91
2 changed files with 55 additions and 12 deletions
@@ -2,10 +2,16 @@ import { useState } from "react";
import Row from "@moduletrace/twui/layout/Row"; import Row from "@moduletrace/twui/layout/Row";
import Span from "@moduletrace/twui/layout/Span"; import Span from "@moduletrace/twui/layout/Span";
import Stack from "@moduletrace/twui/layout/Stack"; import Stack from "@moduletrace/twui/layout/Stack";
import type { EnvScope, EnvsMutationResult, KnownEnvStatus, PublicEnv } from "@/src/types"; import type {
EnvScope,
EnvsMutationResult,
KnownEnvStatus,
PublicEnv,
} from "@/src/types";
import postEnvApi from "../../(functions)/post-env-api"; import postEnvApi from "../../(functions)/post-env-api";
import EnvForm from "./env-form"; import EnvForm from "./env-form";
import EnvRowActions from "./env-row-actions"; import EnvRowActions from "./env-row-actions";
import { twMerge } from "tailwind-merge";
type Props = { type Props = {
scope: EnvScope; scope: EnvScope;
@@ -27,14 +33,26 @@ type Props = {
* One env: its key, description and value (or "secret"), with Edit and * One env: its key, description and value (or "secret"), with Edit and
* Remove. Edit opens the form in place. * Remove. Edit opens the form in place.
*/ */
export default function EnvRow({ scope, env, known, scoped, onChanged, onError }: Props) { export default function EnvRow({
scope,
env,
known,
scoped,
onChanged,
onError,
}: Props) {
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const key = env?.key || known?.key || ""; const key = env?.key || known?.key || "";
const remove = async () => { const remove = async () => {
if (!env || busy) return; if (!env || busy) return;
if (!window.confirm(`Remove ${key}? Computers drop it on their next run.`)) return; if (
!window.confirm(
`Remove ${key}? Computers drop it on their next run.`,
)
)
return;
setBusy(true); setBusy(true);
const res = await postEnvApi({ const res = await postEnvApi({
@@ -43,7 +61,8 @@ export default function EnvRow({ scope, env, known, scoped, onChanged, onError }
}); });
setBusy(false); setBusy(false);
if (res.success && res.singleRes) onChanged(res.singleRes, res.msg || "Removed."); if (res.success && res.singleRes)
onChanged(res.singleRes, res.msg || "Removed.");
else onError(res.msg || "Couldn't remove the env."); else onError(res.msg || "Couldn't remove the env.");
}; };
@@ -72,12 +91,22 @@ export default function EnvRow({ scope, env, known, scoped, onChanged, onError }
? "From the server's environment" ? "From the server's environment"
: "Not set"; : "Not set";
const flags = env && !env.known && !env.expose_to_computers ? " · not on computers" : ""; const flags =
env && !env.known && !env.expose_to_computers
? " · not on computers"
: "";
return ( return (
<Row className="w-full gap-3! flex-nowrap items-start justify-between py-3 border-0 border-b border-solid border-hairline last:border-b-0"> <Row
className={twMerge(
"w-full gap-3! flex-nowrap items-start justify-between py-3 px-4",
"border-0 border-b border-solid border-hairline last:border-b-0",
)}
>
<Stack className="gap-0.5! min-w-0"> <Stack className="gap-0.5! min-w-0">
<Span className="text-sm font-bold leading-5 text-ink truncate">{key}</Span> <Span className="text-sm font-bold leading-5 text-ink truncate">
{key}
</Span>
{env?.description || known?.description ? ( {env?.description || known?.description ? (
<Span className="text-[13px] leading-[18px] text-ink-secondary break-words"> <Span className="text-[13px] leading-[18px] text-ink-secondary break-words">
{known?.description || env?.description} {known?.description || env?.description}
@@ -34,20 +34,34 @@ type Props = {
}; };
const Descriptions: Record<EnvScope["paradigm"], string> = { const Descriptions: Record<EnvScope["paradigm"], string> = {
general: "Set everywhere. A workspace, a bot or a computer can set the same key to override it.", general:
workspace: "For every bot working here. Beats general, loses to the bot's own.", "Set everywhere. A workspace, a bot or a computer can set the same key to override it.",
computer: "For this computer only. Beats a workspace, loses to the bot's own.", workspace:
"For every bot working here. Beats general, loses to the bot's own.",
computer:
"For this computer only. Beats a workspace, loses to the bot's own.",
bot: "For this bot only, everywhere it works. Beats the workspace, the computer and general.", bot: "For this bot only, everywhere it works. Beats the workspace, the computer and general.",
}; };
/** /**
* The envs bots and computers use, in one scope, with an add form. * The envs bots and computers use, in one scope, with an add form.
*/ */
export default function EnvsGroup({ scope, envs, scoped, title, status, onChanged, onError }: Props) { export default function EnvsGroup({
scope,
envs,
scoped,
title,
status,
onChanged,
onError,
}: Props) {
const [adding, setAdding] = useState(false); const [adding, setAdding] = useState(false);
return ( return (
<SettingsGroup title={title || "Envs"} description={Descriptions[scope.paradigm]}> <SettingsGroup
title={title || "Envs"}
description={Descriptions[scope.paradigm]}
>
<SettingsList is_empty={!envs.length} empty="Nothing set here yet."> <SettingsList is_empty={!envs.length} empty="Nothing set here yet.">
{envs.map((env) => ( {envs.map((env) => (
<EnvRow <EnvRow