diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..2bbac33 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + +
+ + +Web UI placeholder
' > dist/index.html" + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "~5.6.3", + "vite": "^6.0.3" } } diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..c90372a --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,194 @@ +/* ── Base ─────────────────────────────────────────────────────────────────── */ + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; + line-height: 1.5; + color: #1a1a2e; + background: #f4f4f8; +} + +/* ── App shell ────────────────────────────────────────────────────────────── */ + +.app-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1.5rem; + background: #1a1a2e; + color: #fff; +} + +.app-header h1 { + font-size: 1.125rem; + font-weight: 600; + letter-spacing: -0.01em; +} + +.app-header nav a { + color: rgba(255, 255, 255, 0.8); + text-decoration: none; + font-size: 0.875rem; +} + +.app-header nav a:hover { + color: #fff; +} + +.app-main { + max-width: 72rem; + margin: 1.5rem auto; + padding: 0 1.5rem; +} + +/* ── Cards ────────────────────────────────────────────────────────────────── */ + +.card { + background: #fff; + border: 1px solid #e2e2e8; + border-radius: 0.5rem; + padding: 1.25rem; + margin-bottom: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); +} + +.card h2 { + font-size: 1rem; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.card p { + font-size: 0.875rem; + color: #555; +} + +/* ── Status badges ────────────────────────────────────────────────────────── */ + +.badge { + display: inline-block; + padding: 0.125rem 0.5rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: capitalize; +} + +.badge--pending { + background: #fef3c7; + color: #92400e; +} + +.badge--approved { + background: #d1fae5; + color: #065f46; +} + +.badge--edited { + background: #dbeafe; + color: #1e40af; +} + +.badge--rejected { + background: #fee2e2; + color: #991b1b; +} + +/* ── Buttons ──────────────────────────────────────────────────────────────── */ + +.btn { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem 0.75rem; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + font-size: 0.8125rem; + font-weight: 500; + cursor: pointer; + background: #fff; + color: #374151; + transition: background 0.15s, border-color 0.15s; +} + +.btn:hover { + background: #f9fafb; + border-color: #9ca3af; +} + +.btn--approve { + background: #059669; + color: #fff; + border-color: #059669; +} + +.btn--approve:hover { + background: #047857; +} + +.btn--reject { + background: #dc2626; + color: #fff; + border-color: #dc2626; +} + +.btn--reject:hover { + background: #b91c1c; +} + +/* ── Mode toggle ──────────────────────────────────────────────────────────── */ + +.mode-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.8125rem; +} + +.mode-toggle__switch { + position: relative; + width: 2.5rem; + height: 1.25rem; + background: #d1d5db; + border: none; + border-radius: 9999px; + cursor: pointer; + transition: background 0.2s; +} + +.mode-toggle__switch--active { + background: #059669; +} + +.mode-toggle__switch::after { + content: ""; + position: absolute; + top: 0.125rem; + left: 0.125rem; + width: 1rem; + height: 1rem; + background: #fff; + border-radius: 50%; + transition: transform 0.2s; +} + +.mode-toggle__switch--active::after { + transform: translateX(1.25rem); +} + +/* ── Loading / empty states ───────────────────────────────────────────────── */ + +.loading { + text-align: center; + padding: 3rem 1rem; + color: #6b7280; + font-size: 0.875rem; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..a121bc5 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,24 @@ +import { Navigate, Route, Routes } from "react-router-dom"; +import ReviewQueue from "./pages/ReviewQueue"; +import MomentDetail from "./pages/MomentDetail"; + +export default function App() { + return ( +Moment ID: {momentId}
No pending moments to review.
+ ) : ( + items.map((item) => ( + ++ {item.creator_name} · {item.video_filename} ·{" "} + {item.start_time.toFixed(1)}s – {item.end_time.toFixed(1)}s +
++ + {item.review_status} + +
+