From 538daea0f146aed02f45e677417a7ee6baaef4f4 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 9 Jul 2026 18:49:55 +0700 Subject: [PATCH] refactor: update dashboard components for improved layout and functionality Refactored the DashboardQuickLinks component to utilize the new DashboardQuickLinkCard for better organization and presentation. Enhanced the DashboardComponent to integrate quick links within a DashboardFramePanel, improving the overall user experience. Updated the metric-tile component import path for consistency. Removed unnecessary imports from the separator component to streamline the codebase. --- .../card-17/components/card-dot-field.tsx | 99 +++++++++++++++++++ .../blocks/card-17/components/card-grid.tsx | 17 ++++ .../blocks/card-17/components/card-item.tsx | 44 +++++++++ .../blocks/card-17/components/data.tsx | 53 ++++++++++ .../src/components/blocks/card-17/page.tsx | 9 ++ .../dashboard-5/components/metric-tile.tsx | 2 +- .../components/dashboard/card-dot-field.tsx | 87 ++++++++++++++++ .../dashboard/dashboard-quick-link-card.tsx | 70 +++++++++++++ .../dashboard/dashboard-quick-links.tsx | 41 +++----- apps/web/src/routes/_auth/dashboard.tsx | 12 +-- apps/web/tsconfig.tsbuildinfo | 2 +- packages/ui/src/components/separator.tsx | 2 - 12 files changed, 401 insertions(+), 37 deletions(-) create mode 100644 apps/web/src/components/blocks/card-17/components/card-dot-field.tsx create mode 100644 apps/web/src/components/blocks/card-17/components/card-grid.tsx create mode 100644 apps/web/src/components/blocks/card-17/components/card-item.tsx create mode 100644 apps/web/src/components/blocks/card-17/components/data.tsx create mode 100644 apps/web/src/components/blocks/card-17/page.tsx create mode 100644 apps/web/src/components/dashboard/card-dot-field.tsx create mode 100644 apps/web/src/components/dashboard/dashboard-quick-link-card.tsx diff --git a/apps/web/src/components/blocks/card-17/components/card-dot-field.tsx b/apps/web/src/components/blocks/card-17/components/card-dot-field.tsx new file mode 100644 index 0000000..6fda68a --- /dev/null +++ b/apps/web/src/components/blocks/card-17/components/card-dot-field.tsx @@ -0,0 +1,99 @@ +"use client" + +import { useEffect, useRef } from "react" + +import { cn } from "@evobgp/ui/lib/utils" + +// Deterministic per-dot value so the field reads as noise, not a flat grid. +function grain(i: number, j: number) { + const n = Math.sin(i * 127.1 + j * 311.7) * 43758.5453 + return n - Math.floor(n) +} + +/** + * Static dot field adapted from auth-3's WaveDots backdrop with the twinkle + * animation removed: a dense grid of dots at a fixed per-dot brightness, + * painted once and again on resize or theme change. The dot color resolves + * from the canvas `color` token so it stays neutral in light and dark. + * customize: GAP (density), DOT (size), PEAK (max brightness). + */ +export function CardDotField({ className }: { className?: string }) { + const canvasRef = useRef(null) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext("2d") + if (!ctx) return + + const GAP = 3 // dot spacing in px (tight grid, still distinct dots) + const DOT = 1.5 // dot side in px + const BASE = 0.05 // dim end of a dot + const PEAK = 0.32 // bright end of a dot (kept subtle for a card surface) + + const draw = () => { + const rect = canvas.getBoundingClientRect() + if (!rect.width || !rect.height) return + + // Resetting width clears the canvas and restores the identity transform, + // so the color probe below reads a raw device pixel. + const dpr = Math.min(window.devicePixelRatio || 1, 2) + canvas.width = Math.round(rect.width * dpr) + canvas.height = Math.round(rect.height * dpr) + + // Resolve the token to concrete sRGB by painting + reading it back, so + // oklch never casts a color and dark mode recolors on theme change. + ctx.fillStyle = getComputedStyle(canvas).color || "rgb(115,115,115)" + ctx.fillRect(0, 0, 1, 1) + const px = ctx.getImageData(0, 0, 1, 1).data + const color = `rgb(${px[0]}, ${px[1]}, ${px[2]})` + + ctx.setTransform(dpr, 0, 0, dpr, 0, 0) + ctx.clearRect(0, 0, rect.width, rect.height) + ctx.fillStyle = color + + const cols = Math.ceil(rect.width / GAP) + 1 + const rows = Math.ceil(rect.height / GAP) + 1 + for (let i = 0; i < cols; i++) { + const x = i * GAP + for (let j = 0; j < rows; j++) { + const q = grain(i, j) + const amp = 0.7 + 0.6 * grain(j * 2 + 1, i * 2 + 1) + let a = (BASE + (PEAK - BASE) * q * q) * amp + if (a > 1) a = 1 + ctx.globalAlpha = a + ctx.fillRect(x, j * GAP, DOT, DOT) + } + } + ctx.globalAlpha = 1 + } + + draw() + + const resizeObserver = new ResizeObserver(() => draw()) + resizeObserver.observe(canvas) + + // Repaint when the theme class toggles so the resolved color stays correct. + const themeObserver = new MutationObserver(() => draw()) + themeObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class", "style"], + }) + + return () => { + resizeObserver.disconnect() + themeObserver.disconnect() + } + }, []) + + return ( +