Phase 1: scaffold Next.js 16 web-ui + Coolify staging
- create-next-app with TypeScript, Tailwind v4, App Router - Port design-system.css tokens into Tailwind @theme (navy/gold/parchment, Heebo) - Install TanStack Query, react-hook-form, zod, lucide-react, react-dropzone - layout.tsx: RTL Hebrew + Heebo via next/font/google - AppShell component with navy header + gold rule + nav - next.config.ts: output:standalone + rewrites to proxy /api/* to production FastAPI - Dockerfile: multi-stage Node 20 Alpine build for Next.js standalone (branch-local override of the FastAPI Dockerfile; main is unaffected) - Switch .taskmaster to claude-code provider (no API key required) - Add 7 phase tasks (83-89) tracking the full rewrite plan Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
BIN
web-ui/src/app/favicon.ico
Normal file
BIN
web-ui/src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
178
web-ui/src/app/globals.css
Normal file
178
web-ui/src/app/globals.css
Normal file
@@ -0,0 +1,178 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
* Ezer Mishpati — Design Tokens (ported from legal-ai/web/static/design-system.css)
|
||||
* Editorial/judicial aesthetic for a Hebrew RTL judicial tool.
|
||||
* Palette: Navy + Cream + Gold.
|
||||
* Typography: Heebo (loaded via next/font/google in layout.tsx).
|
||||
* ══════════════════════════════════════════════════════════════ */
|
||||
|
||||
@theme {
|
||||
/* ── Colors ─────────────────────────────────────────── */
|
||||
--color-navy: #0f172a;
|
||||
--color-navy-soft: #1e293b;
|
||||
--color-navy-dim: #334155;
|
||||
|
||||
--color-cream: #f5f1e8;
|
||||
--color-cream-deep: #ede8d8;
|
||||
--color-parchment: #fbf8f0;
|
||||
|
||||
--color-gold: #a97d3a;
|
||||
--color-gold-deep: #8b6428;
|
||||
--color-gold-soft: #c89a56;
|
||||
--color-gold-wash: #fdf6e8;
|
||||
|
||||
--color-ink: #1a1a2e;
|
||||
--color-ink-soft: #3a3a52;
|
||||
--color-ink-muted: #6b7280;
|
||||
--color-ink-light: #9ca3af;
|
||||
|
||||
--color-rule: #e5dfd0;
|
||||
--color-rule-soft: #f0ead8;
|
||||
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-raised: #fbf8f0;
|
||||
--color-bg: #f5f1e8;
|
||||
|
||||
/* Status colors — tuned to the palette */
|
||||
--color-success: #4a7c59;
|
||||
--color-success-bg: #e8efe7;
|
||||
--color-warn: #b8894a;
|
||||
--color-warn-bg: #faf0dc;
|
||||
--color-danger: #a54242;
|
||||
--color-danger-bg: #f5e6e6;
|
||||
--color-info: #4e6a8c;
|
||||
--color-info-bg: #e6ecf3;
|
||||
|
||||
/* ── Typography — Heebo (loaded via next/font in layout.tsx) ─ */
|
||||
--font-display: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "Cascadia Code", "SF Mono", Menlo, monospace;
|
||||
|
||||
/* ── Radii ──────────────────────────────────────────── */
|
||||
--radius-sm: 4px;
|
||||
--radius: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
|
||||
/* ── Shadows — soft, editorial ──────────────────────── */
|
||||
--shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
--shadow: 0 2px 6px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.04);
|
||||
--shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12), 0 2px 6px rgba(15, 23, 42, 0.05);
|
||||
|
||||
/* ── Transitions ────────────────────────────────────── */
|
||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||
}
|
||||
|
||||
/* ── Dark theme (class-based, toggled on <html> or <body>) ── */
|
||||
:root.dark,
|
||||
body.dark {
|
||||
--color-navy: #f5f1e8;
|
||||
--color-navy-soft: #e8e0c8;
|
||||
--color-navy-dim: #c7bc9a;
|
||||
|
||||
--color-cream: #0a0f1c;
|
||||
--color-cream-deep: #121a2e;
|
||||
--color-parchment: #161f36;
|
||||
|
||||
--color-gold: #d4a55a;
|
||||
--color-gold-deep: #e8bc6f;
|
||||
--color-gold-soft: #c89a56;
|
||||
--color-gold-wash: rgba(212, 165, 90, 0.08);
|
||||
|
||||
--color-ink: #f5f1e8;
|
||||
--color-ink-soft: #d8d2c0;
|
||||
--color-ink-muted: #9a9380;
|
||||
--color-ink-light: #6a6458;
|
||||
|
||||
--color-rule: #2a3352;
|
||||
--color-rule-soft: #1e2a45;
|
||||
|
||||
--color-surface: #141b2f;
|
||||
--color-surface-raised: #1a2238;
|
||||
--color-bg: #0a0f1c;
|
||||
|
||||
--color-success: #5a9a6a;
|
||||
--color-success-bg: rgba(90, 154, 106, 0.12);
|
||||
--color-warn: #c79956;
|
||||
--color-warn-bg: rgba(199, 153, 86, 0.12);
|
||||
--color-danger: #c16565;
|
||||
--color-danger-bg: rgba(193, 101, 101, 0.12);
|
||||
--color-info: #6d8bab;
|
||||
--color-info-bg: rgba(109, 139, 171, 0.12);
|
||||
}
|
||||
|
||||
/* ── Base overrides ──────────────────────────────────── */
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.65;
|
||||
color: var(--color-ink);
|
||||
background: var(--color-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: "kern", "liga", "clig", "calt";
|
||||
}
|
||||
|
||||
/* Display typography — headings use the same Heebo family but bolder */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
color: var(--color-navy);
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
h1 { font-size: 2.3rem; font-weight: 900; }
|
||||
h2 { font-size: 1.8rem; }
|
||||
h3 { font-size: 1.45rem; }
|
||||
h4 { font-size: 1.2rem; }
|
||||
h5 { font-size: 1.05rem; }
|
||||
h6 { font-size: 0.95rem; }
|
||||
|
||||
/* Prose paragraphs — justify both sides for Hebrew legal text */
|
||||
p.prose,
|
||||
.prose p {
|
||||
text-align: justify;
|
||||
text-justify: inter-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
color: var(--color-gold-deep);
|
||||
text-decoration: none;
|
||||
transition: color 120ms var(--ease-out);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--color-gold);
|
||||
}
|
||||
|
||||
/* Focus rings — gold, subtle */
|
||||
*:focus-visible {
|
||||
outline: 2px solid var(--color-gold);
|
||||
outline-offset: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: var(--color-gold-wash);
|
||||
color: var(--color-navy);
|
||||
}
|
||||
27
web-ui/src/app/layout.tsx
Normal file
27
web-ui/src/app/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Heebo } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const heebo = Heebo({
|
||||
variable: "--font-heebo",
|
||||
subsets: ["hebrew", "latin"],
|
||||
weight: ["300", "400", "500", "600", "700", "800", "900"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "עוזר משפטי — ניהול תיקים",
|
||||
description: "מערכת סיוע בניסוח החלטות לוועדת ערר לתכנון ובנייה",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="he" dir="rtl" className={`${heebo.variable} h-full antialiased`}>
|
||||
<body className="min-h-full flex flex-col">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
27
web-ui/src/app/page.tsx
Normal file
27
web-ui/src/app/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { AppShell } from "@/components/app-shell";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<AppShell>
|
||||
<section className="space-y-6">
|
||||
<header className="space-y-2">
|
||||
<h1 className="text-navy">עוזר משפטי</h1>
|
||||
<p className="text-ink-muted text-base max-w-2xl">
|
||||
מערכת סיוע בניסוח החלטות לוועדת ערר לתכנון ובנייה — ניהול תיקים, חיפוש
|
||||
סמנטי, וכלי כתיבה לפי ארכיטקטורת 12 הבלוקים.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<hr className="border-0 h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
|
||||
|
||||
<div className="rounded-lg bg-surface shadow-sm p-6 border border-rule">
|
||||
<p className="text-ink">
|
||||
שלב 1 של תוכנית השדרוג הושלם: scaffold של Next.js 16, פורט של design
|
||||
tokens ל-Tailwind v4, RTL עברית עם Heebo. הפאזות הבאות יביאו את 10
|
||||
המסכים ל-parity עם ה-UI הקיים.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
71
web-ui/src/components/app-shell.tsx
Normal file
71
web-ui/src/components/app-shell.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
/**
|
||||
* Ezer Mishpati navigation shell.
|
||||
*
|
||||
* Editorial/judicial aesthetic:
|
||||
* - Navy header with a gold hairline rule (border-b-3)
|
||||
* - Parchment/cream body background (set on <body> via globals.css)
|
||||
* - Hebrew RTL throughout (set on <html> in layout.tsx)
|
||||
*
|
||||
* Structure mirrors the current vanilla index.html header so that visual
|
||||
* continuity is preserved while we migrate screen-by-screen.
|
||||
*/
|
||||
|
||||
type NavItem = {
|
||||
href: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ href: "/", label: "בית" },
|
||||
{ href: "/upload", label: "העלאת מסמכים" },
|
||||
{ href: "/training", label: "אימון סגנון" },
|
||||
{ href: "/skills", label: "מיומנויות" },
|
||||
{ href: "/diagnostics", label: "אבחון" },
|
||||
];
|
||||
|
||||
export function AppShell({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className="
|
||||
relative z-10 flex items-center gap-4
|
||||
px-10 py-[18px]
|
||||
bg-navy text-parchment
|
||||
border-b-[3px] border-gold
|
||||
shadow-md
|
||||
"
|
||||
>
|
||||
<Link href="/" className="flex items-baseline gap-3 hover:text-parchment">
|
||||
<span className="font-display text-[1.45rem] font-bold tracking-[0.02em] text-parchment">
|
||||
עוזר משפטי
|
||||
</span>
|
||||
<span className="text-gold-soft text-sm font-medium">ניהול תיקים</span>
|
||||
</Link>
|
||||
|
||||
<nav className="me-auto flex items-center gap-1">
|
||||
{NAV_ITEMS.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="
|
||||
px-3 py-1.5 rounded
|
||||
text-sm text-parchment/80
|
||||
transition-colors
|
||||
hover:text-parchment hover:bg-navy-soft/60
|
||||
"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main className="flex-1 w-full max-w-[1400px] mx-auto px-10 py-10">
|
||||
{children}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user