1// Custom hook — Villeto spend dashboard
2export function useSpendData(teamId: string) {
3 const [data, setData] = useState<SpendSummary | null>(null)
4 const { token } = useAuth()
5
6 useEffect(() => {
7 const controller = new AbortController()
8 fetchSpend({ teamId, token, signal: controller.signal })
9 .then(setData).catch(handleError)
10 return () => controller.abort()
11 }, [teamId, token])
12
13 return { data, isLoaded: data !== null }
14}
1// Next.js API route — quote estimate
2export async function POST(req: Request) {
3 const body = await req.json()
4 const pdf = await generateQuotePDF(body)
5
6 await resend.emails.send({
7 from: '[email protected]',
8 to: body.email,
9 attachments: [{ filename: 'quote.pdf', content: pdf }]
10 })
11 return Response.json({ ok: true })
12}
1// Hypacode motion tokens
2export const ease = {
3 entrance: [0.16, 1, 0.3, 1],
4 spring: { type: 'spring', stiffness: 400, damping: 30 },
5}
6export const duration = {
7 xs: 0.1, sm: 0.2,
8 md: 0.4, lg: 0.6, xl: 0.7
9}
10export const stagger = {
11 children: 0.07, fast: 0.04
12}