diff --git a/UI-DESIGN.md b/UI-DESIGN.md index 2db7ff96..30c87cd3 100644 --- a/UI-DESIGN.md +++ b/UI-DESIGN.md @@ -13,6 +13,23 @@ --- +## 🧩 Figma Source + +Figma file: https://www.figma.com/file/qkTJWD2iKj4W2BVztdCrHt/SoroScan-UI-Design?node-id=0-1 + +This repository follows the Figma master file as the single source of truth for colors, typography, spacing, and component behavior. + +--- + +## 📱 Responsive System + +- Mobile: single-column stacked cards, large touch targets, high-contrast controls +- Tablet: grouped panels with compact button rows +- Desktop: wider layouts with side-by-side sections and status surfaces +- Breakpoints: mobile first, then `sm` and `md` scale-up layouts across the UI + +--- + ## 📐 Design System Foundation ### Color Palette @@ -226,6 +243,32 @@ SPECS └──────────────────────────────────────────────────────────┘ ``` +## 📊 Comparison & Diff View + +``` +┌───────────────────────────────────────────────────────────────────────────┐ +│ [EVENT A] │ [EVENT B] │ +├───────────────────────────────────────────────────────────────────────────┤ +│ Timestamp: 2026-05-29 12:24 │ Timestamp: 2026-05-29 12:44 │ +│ Event Type: Transfer │ Event Type: Transfer │ +├───────────────────────────────────────────────────────────────────────────┤ +│ Field │ A Value │ B Value │ Status │ +├───────────────────────────────────────────────────────────────────────────┤ +│ from │ 0xabc...123 │ 0xabc...123 │ unchanged │ +│ to │ 0xdef...456 │ 0xdef...789 │ modified │ +│ amount │ 100 │ 250 │ modified │ +│ memo │ "swap" │ — │ deleted │ +│ reward │ — │ 10 │ added │ +└───────────────────────────────────────────────────────────────────────────┘ +``` + +- Side-by-side layout shows event details in parallel columns. +- Additions: green background, deletions: red, modified: amber, unchanged: neutral. +- Alternative unified diff option uses inline change markers (+/−) with syntax-highlighted JSON/code blocks. +- Mobile stacks event sections vertically with clear headers and toggle buttons to switch between A/B views. +- Includes summary metrics: lines added, removed, changed. +- Supports expandable rows for large payloads and collapsed sections for unchanged fields. + ### 5. Modal / Dialog ``` diff --git a/soroscan-frontend/__tests__/confirmation-dialog.test.tsx b/soroscan-frontend/__tests__/confirmation-dialog.test.tsx new file mode 100644 index 00000000..99d26260 --- /dev/null +++ b/soroscan-frontend/__tests__/confirmation-dialog.test.tsx @@ -0,0 +1,43 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { ConfirmationDialog } from "../components/ui/confirmation-dialog"; + +describe("ConfirmationDialog", () => { + it("renders title, description, and buttons", () => { + render( + + ); + + expect(screen.getByText("Delete API key")).toBeInTheDocument(); + expect(screen.getByText("This action cannot be undone.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /revoke/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /keep/i })).toBeInTheDocument(); + }); + + it("calls callbacks when buttons are clicked", () => { + const onConfirm = jest.fn(); + const onCancel = jest.fn(); + + render( + + ); + + fireEvent.click(screen.getByRole("button", { name: /cancel/i })); + expect(onCancel).toHaveBeenCalledTimes(1); + + fireEvent.click(screen.getByRole("button", { name: /confirm/i })); + expect(onConfirm).toHaveBeenCalledTimes(1); + }); +}); diff --git a/soroscan-frontend/app/settings/components/APIKeyManager.tsx b/soroscan-frontend/app/settings/components/APIKeyManager.tsx index 4bd9f00c..242c0f47 100644 --- a/soroscan-frontend/app/settings/components/APIKeyManager.tsx +++ b/soroscan-frontend/app/settings/components/APIKeyManager.tsx @@ -1,5 +1,6 @@ "use client"; import { useState } from "react"; +import { ConfirmationDialog } from "@/components/ui/confirmation-dialog"; type APIKey = { id: string; @@ -30,6 +31,8 @@ export default function APIKeyManager() { return []; }); const [copied, setCopied] = useState(null); + const [confirmingKey, setConfirmingKey] = useState(null); + const [isRevoking, setIsRevoking] = useState(false); const saveKeys = (newKeys: APIKey[]) => { setKeys(newKeys); @@ -49,8 +52,15 @@ export default function APIKeyManager() { saveKeys([...keys, newKey]); }; - const handleRevoke = (id: string) => { - saveKeys(keys.filter((k) => k.id !== id)); + const requestRevoke = (id: string) => setConfirmingKey(id); + + const handleConfirmRevoke = () => { + if (!confirmingKey) return; + setIsRevoking(true); + const nextKeys = keys.filter((k) => k.id !== confirmingKey); + saveKeys(nextKeys); + setConfirmingKey(null); + setIsRevoking(false); }; const handleCopy = (key: string) => { diff --git a/soroscan-frontend/app/settings/components/NotificationPrefs.tsx b/soroscan-frontend/app/settings/components/NotificationPrefs.tsx index 0eae9a72..440a6fe8 100644 --- a/soroscan-frontend/app/settings/components/NotificationPrefs.tsx +++ b/soroscan-frontend/app/settings/components/NotificationPrefs.tsx @@ -54,6 +54,7 @@ export default function NotificationPrefs() {
{label} + +
+ + + + ); +}