Aller au contenu
Whoa UI

TemplatesDashboard

Dashboard 1

Un dashboard d'approbations avec navigation, liste filtrable et panneau de détail.

BetaWhoa UI est en bêta : certains éléments peuvent encore changer. Un bug ? Écrivez-moi à hello@ugolin-olle.com.

Plenum
AM

Approvals /Inbox

Approval inbox

Agents prepare the evidence, you decide

On you
2
Value
$199,200
Past 2 days
1

On you

On your team

BC-0193 · Wire and cable budgetDue today

St. Clare Medical Pavilion · cost code 26 05 19 · requested by Evan Brooks 7:52 AM · drafted by Cost Controller

+$12,800

Line budget $274,000 to $286,800

  1. REQ-3108 drafted

    Buyer 7:44

  2. EB

    Evan approved PO

    PM limit · 8:05

  3. AM

    You: budget change

    Waiting on you

  4. PO-10462 issued

    After approval

Budget line 26 05 19

USD · committed, this purchase and forecast to complete

Budget $274,000New $286,800
  • Committed$261,860
  • This purchase$8,940
  • Forecast to complete$16,000

Evidence

Read only
  • REQ-3108 · draft requisition Buyer

    6,000 ft MC cable 12/2 AL · need Oct 30 · L2 imaging

  • RFQ-0412 · 3 quotes Sourcing

    Rampart $8,940 · 5 d · Front Range $8,610 · 12 d · Mile High $9,380 · 3 d

  • Lookahead · L2 branch rough in Planner

    Starts Oct 30 · crew of 6 · 8,400 ft required

  • Stock check · WH-01 Allocator

    2,400 ft available after reservations · reel R-114

EB
Evan Brooks PM · note on REQ-3108 · 7:58
Rampart is the only supplier that makes Oct 30. Mile High is cheaper but lands Nov 2, after L2 rough in starts, so the crew would sit for three days.

What happens nextBuyer issues PO-10462 to Rampart for $8,940, arriving at WH-01 on Oct 23.

Installation#

Via shadcn — copie le code source

pnpm dlx shadcn@latest add https://ugolin-olle.com/r/dashboard-1.json
Installation manuelle
Files
components/blocks/dashboard-1/index.tsx
'use client';import { LazyMotion, MotionConfig, domMax, m } from 'motion/react';import * as React from 'react';import { cn } from '@/lib/utils';import { ApprovalDetail } from './approval-detail';import { ApprovalList } from './approval-list';import {  approvals as defaultApprovals,  details as defaultDetails,  stats as defaultStats,} from './data';import type { Approval, ApprovalDetailData, Stat } from './data';import { enter, rise } from './motion';import { SubHeader } from './sub-header';import { TopNav } from './top-nav';export type { Approval, ApprovalDetailData, Stat } from './data';export { ApprovalCard } from './approval-card';export { ApprovalDetail } from './approval-detail';export { ApprovalList } from './approval-list';export { BudgetBar } from './budget-bar';export { EvidenceList } from './evidence-list';export { SubHeader } from './sub-header';export { TopNav } from './top-nav';export { WorkflowTimeline } from './workflow-timeline';export interface Dashboard1Props {  approvals?: Approval[];  /** Full detail content, keyed by approval id. */  details?: Record<string, ApprovalDetailData>;  stats?: Stat[];  defaultSelectedId?: string;  onSelect?: (id: string) => void;  /** Fill the parent (which must have a height) instead of the viewport, e.g. in docs previews. */  contained?: boolean;  className?: string;}export function Dashboard1({  approvals = defaultApprovals,  details = defaultDetails,  stats = defaultStats,  defaultSelectedId,  onSelect,  contained = false,  className,}: Dashboard1Props) {  const [selectedId, setSelectedId] = React.useState(defaultSelectedId ?? approvals[0]?.id ?? '');  const selected = approvals.find((a) => a.id === selectedId) ?? approvals[0];  const [interacted, setInteracted] = React.useState(false);  const handleSelect = (id: string) => {    setInteracted(true);    setSelectedId(id);    onSelect?.(id);  };  return (    <LazyMotion features={domMax} strict>      <MotionConfig reducedMotion="user" transition={enter}>        <div          className={cn(            'flex flex-col gap-3 bg-slate-200/70 p-3 text-slate-900',            contained ? 'h-full min-h-0' : 'h-screen',            className,          )}        >          <m.header            animate="visible"            className="shrink-0 rounded-[28px] bg-linear-to-br from-[#1b2a5c] via-[#16245a] to-[#1d3fa8]"            initial="hidden"            variants={rise}          >            <TopNav />            <SubHeader stats={stats} />          </m.header>          <main className="grid min-h-0 flex-1 grid-cols-1 gap-3 overflow-y-auto min-[1100px]:grid-cols-[minmax(320px,1fr)_2fr] min-[1100px]:overflow-hidden">            <ApprovalList              approvals={approvals}              onSelect={handleSelect}              selectedId={selected?.id ?? ''}            />            {selected && (              <ApprovalDetail                approval={selected}                detail={details[selected.id]}                key={selected.id}                quick={interacted}              />            )}          </main>        </div>      </MotionConfig>    </LazyMotion>  );}

Le template Dashboard 1 est une boîte de réception d'approbations : barre de navigation, en-tête avec statistiques, liste filtrable (onglets et recherche) et panneau de détail avec chronologie du workflow, barre de budget, preuves et actions de décision.

Installation en CLI

npx @whoa-studio/templates add dashboard-1

Utilisation

Passez vos approvals, vos details (contenu complet, indexé par identifiant) et vos stats. Sans props, le composant affiche des données de démonstration.

Inspiration