feat(ui): IA redesign → production · יישום נאמן של 16 הדפים הנותרים למוקאפים
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
תיקון הגישה: יישום מלא ונאמן של עיצוב-המוקאפים המאושרים (Claude Design) על כל הדפים — שינוי-הרכב אמיתי פר-מוקאפ, לא ליטוש-טוקנים. כל hook/query/mutation/טאב/ טופס/נתון נשמר (אומת: tsc נקי + בדיקת-נוכחות hooks קריטיים; 0 פונקציונליות נמחקה). דפים (← מוקאפ): - בית — לוח: KPI + "תיקים לפי סטטוס" (bars) + כרטיס-אישורים + CTA כפול. - ארכיון — filter-bar שטוח + טבלה נקייה + צ'יפי-סוג/תוצאה. - הערות יו״ר — פריסה דו-טורית + טופס-הוספה חי + כרטיסי-הערה. - ספריית-פסיקה — tabs קו-תחתון + כרטיסי-תוצאה halacha/קטע + AuthorityBadge. - דף-תקדים — באנר-meta parchment + דו-טורי + provenance pills. - פסיקה-חסרה — pill פתוחים + צ'יפי-סטטוס + CTA העלאה. - יומונים — אזור-העלאה מקווקו + כרטיסי-digest + "ממתין" כתווית פסיבית. - גרף — פאנל-צד שכבות/אנליטיקה + canvas parchment. - אימון-סגנון — פורטרט: banner + KPI + אנטומיה + ביטויי-חתימה. - מתודולוגיה — עורך-צ'קליסט + "חל על:" + canon chip. - מיומנויות/סקריפטים — טבלאות אמיתיות + צ'יפי-סטטוס. - הגדרות — sidenav דו-טורי + env-rows עם "ממתין ל-redeploy". - דף-תיק — באנר-תיק parchment + tabs + timeline + "פתח עורך החלטה". - תפעול — SectionHeaders + טבלת-שירותים + כרטיסי-שער gold-wash. - compose — באנר-תיק + SOT pill + פריסה דו-טורית + "השלמה והעברה". תיקונים שלי אחרי הסוכנים: documents-panel (הוצאת רכיב Shell מ-render — React Compiler), scripts useMemo deps. /approvals כבר נבנה מחדש נאמנה (commit קודם). בדיקות: npx tsc --noEmit ✓ · eslint ✓ (לבד מ-learning-panel:109 קיים-מראש). שימור-פונקציונליות אומת. CI Docker build = שער סופי לפני deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { AlertTriangle, CheckCircle2, HelpCircle } from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
type Props = {
|
||||
drift: boolean;
|
||||
@@ -9,31 +8,31 @@ type Props = {
|
||||
coolifyAvailable?: boolean;
|
||||
};
|
||||
|
||||
// Filled pill chips matching IA-redesign mockup 15 (.c-synced / .c-drift).
|
||||
export function DriftBadge({ drift, coolifyAvailable = true }: Props) {
|
||||
if (!coolifyAvailable) {
|
||||
return (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-ink-muted border-rule gap-1"
|
||||
<span
|
||||
className="inline-flex items-center gap-1 rounded-full bg-rule-soft text-ink-muted text-[0.72rem] font-semibold px-2.5 py-0.5"
|
||||
title="Coolify לא זמין — מצב ה-drift לא ידוע"
|
||||
>
|
||||
<HelpCircle className="w-3 h-3" />
|
||||
Unknown
|
||||
</Badge>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (drift) {
|
||||
return (
|
||||
<Badge variant="outline" className="text-warn border-warn/40 gap-1">
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-warn-bg text-warn text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
<AlertTriangle className="w-3 h-3" />
|
||||
Drift
|
||||
</Badge>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Badge variant="outline" className="text-success border-success/40 gap-1">
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-success-bg text-success text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
<CheckCircle2 className="w-3 h-3" />
|
||||
Synced
|
||||
</Badge>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { useState } from "react";
|
||||
import { ExternalLink, Save, Lock } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import type { McpEnvVar } from "@/lib/api/settings";
|
||||
import { useUpdateMcpEnv } from "@/lib/api/settings";
|
||||
import { toast } from "sonner";
|
||||
@@ -44,36 +43,38 @@ export function EnvVarRow({
|
||||
`https://coolify.nautilus.marcusgroup.org/project/applications/${coolifyAppUuid}/environment-variables`;
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-rule p-4 bg-rule-soft/20 hover:bg-rule-soft/40 transition-colors">
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<code className="font-mono text-sm font-medium text-navy" dir="ltr">
|
||||
{spec.key}
|
||||
</code>
|
||||
<Badge variant="outline" className="text-[0.7rem]">
|
||||
{spec.type}
|
||||
</Badge>
|
||||
{spec.is_secret && (
|
||||
<Badge variant="outline" className="text-[0.7rem] text-warn border-warn/40 gap-1">
|
||||
<Lock className="w-3 h-3" />
|
||||
secret
|
||||
</Badge>
|
||||
)}
|
||||
<DriftBadge drift={spec.drift} coolifyAvailable={coolifyAvailable} />
|
||||
{spec.has_duplicates && (
|
||||
<Badge variant="outline" className="text-[0.7rem] text-warn border-warn/40">
|
||||
duplicates
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-ink-muted mt-1">{spec.description}</p>
|
||||
</div>
|
||||
<div className="px-5 py-4 border-b border-rule-soft last:border-b-0">
|
||||
{/* envtop — key + type chip + secret chip + drift/synced chip */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<code className="font-mono text-[0.84rem] font-semibold text-navy" dir="ltr">
|
||||
{spec.key}
|
||||
</code>
|
||||
<span className="inline-flex items-center rounded-full bg-info-bg text-info text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
{spec.type}
|
||||
</span>
|
||||
{spec.is_secret && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-navy text-white text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
<Lock className="w-3 h-3" />
|
||||
secret
|
||||
</span>
|
||||
)}
|
||||
<DriftBadge drift={spec.drift} coolifyAvailable={coolifyAvailable} />
|
||||
{spec.has_duplicates && (
|
||||
<span className="inline-flex items-center rounded-full bg-warn-bg text-warn text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
duplicates
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{spec.description && (
|
||||
<p className="text-[0.82rem] text-ink-muted mt-1">{spec.description}</p>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[0.72rem] text-ink-muted w-20">Coolify:</span>
|
||||
{/* vals — Coolify value | Container value (+pending) | save (mockup .vals 3-col) */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-[1fr_1fr_auto] gap-3.5 items-end mt-3.5">
|
||||
<div>
|
||||
<span className="block text-[0.72rem] text-ink-muted font-semibold mb-1">
|
||||
Coolify:
|
||||
</span>
|
||||
{spec.is_editable ? (
|
||||
<EnvVarEditor
|
||||
spec={spec}
|
||||
@@ -82,53 +83,61 @@ export function EnvVarRow({
|
||||
disabled={update.isPending}
|
||||
/>
|
||||
) : (
|
||||
<span className="font-mono text-ink" dir="ltr">
|
||||
{spec.coolify_value ?? <em className="text-ink-muted">— לא מוגדר —</em>}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="text-[0.72rem] text-ink-muted w-20">Container:</span>
|
||||
<span className="font-mono text-ink" dir="ltr">
|
||||
{spec.container_value ?? <em className="text-ink-muted">— לא מוגדר —</em>}
|
||||
</span>
|
||||
{/* ADM-5 (INV-IA5/INV-IA6): when Coolify ≠ Container the container is
|
||||
running a stale value until a redeploy — say so in plain Hebrew
|
||||
right here, not only via the top "Drift" badge. */}
|
||||
{coolifyAvailable && spec.drift && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-[0.7rem] text-warn border-warn/40"
|
||||
title="הערך נשמר ב-Coolify אך הקונטיינר עדיין מריץ את הקודם — נדרש redeploy"
|
||||
<a
|
||||
href={coolifyEnvUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-mono text-[0.81rem] text-gold-deep hover:underline flex items-center gap-1"
|
||||
dir="ltr"
|
||||
>
|
||||
ממתין ל-redeploy
|
||||
</Badge>
|
||||
{spec.coolify_value ?? "— לא מוגדר —"}
|
||||
<ExternalLink className="w-3 h-3 shrink-0" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="block text-[0.72rem] text-ink-muted font-semibold mb-1">
|
||||
Container:
|
||||
</span>
|
||||
<div className="font-mono text-[0.81rem] text-ink-soft bg-rule-soft rounded-md px-3 py-2 flex items-center gap-2 flex-wrap" dir="ltr">
|
||||
<span>{spec.container_value ?? "— לא מוגדר —"}</span>
|
||||
{/* ADM-5 (INV-IA5/INV-IA6): Coolify ≠ Container ⇒ container is stale
|
||||
until a redeploy — say so in plain Hebrew right here. */}
|
||||
{coolifyAvailable && spec.drift && (
|
||||
<span
|
||||
className="inline-flex items-center rounded-full bg-warn-bg text-warn border border-warn text-[0.7rem] font-semibold px-2 py-0.5"
|
||||
dir="rtl"
|
||||
title="הערך נשמר ב-Coolify אך הקונטיינר עדיין מריץ את הקודם — נדרש redeploy"
|
||||
>
|
||||
ממתין ל-redeploy
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex md:block items-end">
|
||||
{spec.is_editable ? (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={!dirty || update.isPending}
|
||||
variant={dirty ? "default" : "outline"}
|
||||
className={dirty ? "bg-gold text-white hover:bg-gold-deep border-transparent" : ""}
|
||||
>
|
||||
<Save className="w-3.5 h-3.5" data-icon="inline-start" />
|
||||
{update.isPending ? "שומר..." : "שמור"}
|
||||
</Button>
|
||||
) : (
|
||||
<a
|
||||
href={coolifyEnvUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 rounded-md border border-rule text-navy px-4 py-1.5 text-[0.81rem] font-semibold hover:bg-gold-wash"
|
||||
>
|
||||
ערוך ב-Coolify
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end gap-2 mt-3">
|
||||
{!spec.is_editable && (
|
||||
<a
|
||||
href={coolifyEnvUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[0.78rem] text-gold-deep hover:underline flex items-center gap-1"
|
||||
>
|
||||
ערוך ב-Coolify
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
)}
|
||||
{spec.is_editable && (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={!dirty || update.isPending}
|
||||
>
|
||||
<Save className="w-3.5 h-3.5" data-icon="inline-start" />
|
||||
{update.isPending ? "שומר..." : "שמור"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -72,37 +72,39 @@ export function EnvironmentTab() {
|
||||
const duplicatesCount = data.vars.filter((v) => v.has_duplicates).length;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card className="bg-surface border-rule">
|
||||
<div className="space-y-5">
|
||||
{/* summary band — Coolify app id + drift/dup counts + redeploy CTA */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<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">
|
||||
<Badge variant="outline">
|
||||
Coolify app: <code dir="ltr" className="ms-1">{data.coolify_app_uuid.slice(0, 8)}…</code>
|
||||
</Badge>
|
||||
{driftCount > 0 && (
|
||||
<Badge variant="outline" className="text-warn border-warn/40">
|
||||
{driftCount} drift
|
||||
</Badge>
|
||||
<span className="inline-flex items-center rounded-full bg-warn-bg text-warn text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
{driftCount} ב-Drift
|
||||
</span>
|
||||
)}
|
||||
{duplicatesCount > 0 && (
|
||||
<Badge variant="outline" className="text-warn border-warn/40">
|
||||
<span className="inline-flex items-center rounded-full bg-warn-bg text-warn text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
{duplicatesCount} duplicates
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
{data.errors.length > 0 && (
|
||||
<Badge variant="outline" className="text-danger border-danger/40">
|
||||
<span className="inline-flex items-center rounded-full bg-danger-bg text-danger text-[0.72rem] font-semibold px-2.5 py-0.5">
|
||||
{data.errors.join(", ")}
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleRedeploy}
|
||||
disabled={redeploy.isPending}
|
||||
variant={pendingRedeploy ? "default" : "outline"}
|
||||
size="sm"
|
||||
className={pendingRedeploy ? "bg-gold text-white hover:bg-gold-deep border-transparent" : ""}
|
||||
variant={pendingRedeploy ? "default" : "outline"}
|
||||
>
|
||||
<RefreshCw className={redeploy.isPending ? "w-3.5 h-3.5 animate-spin" : "w-3.5 h-3.5"} data-icon="inline-start" />
|
||||
{redeploy.isPending ? "Redeploying..." : "Redeploy now"}
|
||||
{redeploy.isPending ? "פורס מחדש..." : "פרוס מחדש"}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -110,28 +112,41 @@ export function EnvironmentTab() {
|
||||
{CATEGORY_ORDER.map((cat) => {
|
||||
const vars = grouped.get(cat);
|
||||
if (!vars || vars.length === 0) return null;
|
||||
const catDrift = vars.filter((v) => v.drift).length;
|
||||
return (
|
||||
<Card key={cat} className="bg-surface border-rule">
|
||||
<CardContent className="px-6 py-5">
|
||||
<h2 className="text-navy text-lg mb-4 flex items-center gap-2">
|
||||
{CATEGORY_LABELS[cat]}
|
||||
<Badge variant="outline" className="text-[0.7rem] tabular-nums">
|
||||
{vars.length}
|
||||
</Badge>
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{vars.map((v) => (
|
||||
<EnvVarRow
|
||||
key={v.key}
|
||||
spec={v}
|
||||
coolifyAppUuid={data.coolify_app_uuid}
|
||||
coolifyAvailable={coolifyAvailable}
|
||||
onPendingRedeploy={() => setPendingRedeploy(true)}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
key={cat}
|
||||
className="rounded-lg border border-rule bg-surface shadow-sm overflow-hidden"
|
||||
>
|
||||
{/* panel header (.ph) — parchment band: title + count + drift chip */}
|
||||
<div className="flex items-center justify-between gap-3 px-5 py-4 border-b border-rule bg-parchment">
|
||||
<div>
|
||||
<h2 className="text-navy text-base font-semibold mb-0 flex items-center gap-2">
|
||||
{CATEGORY_LABELS[cat]}
|
||||
<span className="text-[0.72rem] text-ink-muted font-medium tabular-nums">
|
||||
({vars.length})
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-[0.78rem] text-ink-muted mt-0.5">
|
||||
מקור-האמת הוא Coolify. שינוי נכנס לתוקף רק לאחר redeploy של הקונטיינר.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{coolifyAvailable && catDrift > 0 && (
|
||||
<span className="inline-flex items-center rounded-full bg-warn-bg text-warn text-[0.72rem] font-semibold px-2.5 py-0.5 shrink-0">
|
||||
{catDrift} ב-Drift
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{vars.map((v) => (
|
||||
<EnvVarRow
|
||||
key={v.key}
|
||||
spec={v}
|
||||
coolifyAppUuid={data.coolify_app_uuid}
|
||||
coolifyAvailable={coolifyAvailable}
|
||||
onPendingRedeploy={() => setPendingRedeploy(true)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user