Files
claudemesh/apps/web/src/modules/dashboard/universe/reveal.tsx
Alejandro Gutiérrez 0664180a54
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
feat(web): universe dashboard — meshes + incoming invitations
New /dashboard landing that surfaces meshes and invitations-to-you
in one view. Replaces the simple mesh grid at /dashboard (preserved
at /dashboard/legacy).

Backend additions:
- GET /api/my/invites/incoming — pending_invite rows addressed to
  the authed user's email, joined with invite for role + expiry and
  user/mesh for display. Unaccepted + unrevoked + unexpired only.
- DELETE /api/my/invites/incoming/:id — dismiss a pending invite
  (revokes the pending_invite row only; underlying invite code stays
  valid so the inviter can re-send).

Web additions (all under apps/web/src/modules/dashboard/universe/):
- welcome.tsx — editorial serif header with mesh + invite counts
- invitations.tsx — client card with Accept (→ /i/:code claim flow)
  and optimistic Decline
- meshes-grid.tsx — hero card + compact grid, linked to mesh detail
- reveal.tsx — fade-up motion matching marketing _reveal.tsx

Styling uses the existing claudemesh design tokens (--cm-clay,
--cm-bg-elevated, Anthropic Sans/Serif/Mono) — nothing redefined.

Onboarding redirect (0 meshes → /meshes/new?onboarding=1) preserved,
now gated on 0 invitations too so users with pending invites still
land on the dashboard.

Sidebar icon switched to Atom for the "universe" concept.

Standalone prototype saved at prototypes/live-dashboard.html for
reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:31:15 +01:00

39 lines
686 B
TypeScript

"use client";
import { motion, type Variants } from "motion/react";
import type { ReactNode } from "react";
const fade: Variants = {
hidden: { opacity: 0, y: 20, filter: "blur(4px)" },
visible: (i: number = 0) => ({
opacity: 1,
y: 0,
filter: "blur(0px)",
transition: {
duration: 0.7,
ease: [0.22, 0.61, 0.36, 1],
delay: i * 0.08,
},
}),
};
export const Reveal = ({
children,
delay = 0,
className,
}: {
children: ReactNode;
delay?: number;
className?: string;
}) => (
<motion.div
className={className}
variants={fade}
initial="hidden"
animate="visible"
custom={delay}
>
{children}
</motion.div>
);