feat(settings): wire frontend to Coolify SoT response shape

- McpEnvVar: infisical_value → coolify_value + has_duplicates
- McpEnvResponse: drop Infisical metadata fields
- EnvVarRow: 'Coolify:' label, 'ערוך ב-Coolify' external link
- DriftBadge: infisicalAvailable → coolifyAvailable
- EnvironmentTab: Coolify app badge, duplicates count
This commit is contained in:
2026-05-04 07:53:27 +00:00
parent d1e12619d4
commit ae35934383
4 changed files with 35 additions and 32 deletions

View File

@@ -5,17 +5,17 @@ import { Badge } from "@/components/ui/badge";
type Props = { type Props = {
drift: boolean; drift: boolean;
// When false, Infisical was unreachable: drift state is unknown, not "synced". // When false, Coolify was unreachable: drift state is unknown, not "synced".
infisicalAvailable?: boolean; coolifyAvailable?: boolean;
}; };
export function DriftBadge({ drift, infisicalAvailable = true }: Props) { export function DriftBadge({ drift, coolifyAvailable = true }: Props) {
if (!infisicalAvailable) { if (!coolifyAvailable) {
return ( return (
<Badge <Badge
variant="outline" variant="outline"
className="text-ink-muted border-rule gap-1" className="text-ink-muted border-rule gap-1"
title="Infisical לא זמין — מצב ה-drift לא ידוע" title="Coolify לא זמין — מצב ה-drift לא ידוע"
> >
<HelpCircle className="w-3 h-3" /> <HelpCircle className="w-3 h-3" />
Unknown Unknown

View File

@@ -12,22 +12,20 @@ import { EnvVarEditor } from "./env-var-editor";
type Props = { type Props = {
spec: McpEnvVar; spec: McpEnvVar;
infisicalProjectId: string; coolifyAppUuid: string;
infisicalEnv: string; coolifyAvailable: boolean;
infisicalAvailable: boolean;
onPendingRedeploy: () => void; onPendingRedeploy: () => void;
}; };
export function EnvVarRow({ export function EnvVarRow({
spec, spec,
infisicalProjectId, coolifyAppUuid,
infisicalEnv, coolifyAvailable,
infisicalAvailable,
onPendingRedeploy, onPendingRedeploy,
}: Props) { }: Props) {
const [draft, setDraft] = useState<string>(spec.infisical_value ?? ""); const [draft, setDraft] = useState<string>(spec.coolify_value ?? "");
const update = useUpdateMcpEnv(); const update = useUpdateMcpEnv();
const dirty = draft !== (spec.infisical_value ?? ""); const dirty = draft !== (spec.coolify_value ?? "");
function handleSave() { function handleSave() {
update.mutate( update.mutate(
@@ -42,8 +40,8 @@ export function EnvVarRow({
); );
} }
const infisicalUrl = const coolifyEnvUrl =
`https://secret.dev.marcus-law.co.il/project/${infisicalProjectId}/secrets/overview?env=${infisicalEnv}`; `https://coolify.nautilus.marcusgroup.org/project/applications/${coolifyAppUuid}/environment-variables`;
return ( return (
<div className="rounded-md border border-rule p-4 bg-rule-soft/20 hover:bg-rule-soft/40 transition-colors"> <div className="rounded-md border border-rule p-4 bg-rule-soft/20 hover:bg-rule-soft/40 transition-colors">
@@ -62,7 +60,12 @@ export function EnvVarRow({
secret secret
</Badge> </Badge>
)} )}
<DriftBadge drift={spec.drift} infisicalAvailable={infisicalAvailable} /> <DriftBadge drift={spec.drift} coolifyAvailable={coolifyAvailable} />
{spec.has_duplicates && (
<Badge variant="outline" className="text-[0.7rem] text-warn border-warn/40">
duplicates
</Badge>
)}
</div> </div>
<p className="text-sm text-ink-muted mt-1">{spec.description}</p> <p className="text-sm text-ink-muted mt-1">{spec.description}</p>
</div> </div>
@@ -70,7 +73,7 @@ export function EnvVarRow({
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm"> <div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-[0.72rem] text-ink-muted w-20">Infisical:</span> <span className="text-[0.72rem] text-ink-muted w-20">Coolify:</span>
{spec.is_editable ? ( {spec.is_editable ? (
<EnvVarEditor <EnvVarEditor
spec={spec} spec={spec}
@@ -80,7 +83,7 @@ export function EnvVarRow({
/> />
) : ( ) : (
<span className="font-mono text-ink" dir="ltr"> <span className="font-mono text-ink" dir="ltr">
{spec.infisical_value ?? <em className="text-ink-muted"> לא מוגדר </em>} {spec.coolify_value ?? <em className="text-ink-muted"> לא מוגדר </em>}
</span> </span>
)} )}
</div> </div>
@@ -95,12 +98,12 @@ export function EnvVarRow({
<div className="flex items-center justify-end gap-2 mt-3"> <div className="flex items-center justify-end gap-2 mt-3">
{!spec.is_editable && ( {!spec.is_editable && (
<a <a
href={infisicalUrl} href={coolifyEnvUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-[0.78rem] text-gold-deep hover:underline flex items-center gap-1" className="text-[0.78rem] text-gold-deep hover:underline flex items-center gap-1"
> >
ערוך ב-Infisical ערוך ב-Coolify
<ExternalLink className="w-3 h-3" /> <ExternalLink className="w-3 h-3" />
</a> </a>
)} )}

View File

@@ -67,8 +67,9 @@ export function EnvironmentTab() {
} }
if (!data) return null; if (!data) return null;
const infisicalAvailable = data.errors.length === 0; const coolifyAvailable = data.errors.length === 0;
const driftCount = data.vars.filter((v) => v.drift).length; const driftCount = data.vars.filter((v) => v.drift).length;
const duplicatesCount = data.vars.filter((v) => v.has_duplicates).length;
return ( return (
<div className="space-y-4"> <div className="space-y-4">
@@ -76,16 +77,18 @@ export function EnvironmentTab() {
<CardContent className="px-6 py-4 flex items-center justify-between gap-4 flex-wrap"> <CardContent className="px-6 py-4 flex items-center justify-between gap-4 flex-wrap">
<div className="flex items-center gap-3 flex-wrap text-sm"> <div className="flex items-center gap-3 flex-wrap text-sm">
<Badge variant="outline"> <Badge variant="outline">
Infisical: <code dir="ltr" className="ms-1">{data.infisical_environment}</code> Coolify app: <code dir="ltr" className="ms-1">{data.coolify_app_uuid.slice(0, 8)}</code>
</Badge>
<Badge variant="outline">
Path: <code dir="ltr" className="ms-1">{data.infisical_path}</code>
</Badge> </Badge>
{driftCount > 0 && ( {driftCount > 0 && (
<Badge variant="outline" className="text-warn border-warn/40"> <Badge variant="outline" className="text-warn border-warn/40">
{driftCount} drift {driftCount} drift
</Badge> </Badge>
)} )}
{duplicatesCount > 0 && (
<Badge variant="outline" className="text-warn border-warn/40">
{duplicatesCount} duplicates
</Badge>
)}
{data.errors.length > 0 && ( {data.errors.length > 0 && (
<Badge variant="outline" className="text-danger border-danger/40"> <Badge variant="outline" className="text-danger border-danger/40">
{data.errors.join(", ")} {data.errors.join(", ")}
@@ -121,9 +124,8 @@ export function EnvironmentTab() {
<EnvVarRow <EnvVarRow
key={v.key} key={v.key}
spec={v} spec={v}
infisicalProjectId={data.infisical_project_id} coolifyAppUuid={data.coolify_app_uuid}
infisicalEnv={data.infisical_environment} coolifyAvailable={coolifyAvailable}
infisicalAvailable={infisicalAvailable}
onPendingRedeploy={() => setPendingRedeploy(true)} onPendingRedeploy={() => setPendingRedeploy(true)}
/> />
))} ))}

View File

@@ -79,16 +79,14 @@ export type McpEnvVar = {
min: number | null; min: number | null;
max: number | null; max: number | null;
enum_values: string[] | null; enum_values: string[] | null;
infisical_value: string | null; coolify_value: string | null;
container_value: string | null; container_value: string | null;
drift: boolean; drift: boolean;
has_duplicates: boolean;
}; };
export type McpEnvResponse = { export type McpEnvResponse = {
vars: McpEnvVar[]; vars: McpEnvVar[];
infisical_environment: string;
infisical_project_id: string;
infisical_path: string;
coolify_app_uuid: string; coolify_app_uuid: string;
errors: string[]; errors: string[];
}; };