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>
This commit is contained in:
38
apps/web/src/modules/dashboard/universe/reveal.tsx
Normal file
38
apps/web/src/modules/dashboard/universe/reveal.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
"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>
|
||||
);
|
||||
Reference in New Issue
Block a user