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>
39 lines
686 B
TypeScript
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>
|
|
);
|