feat: complete Phase 3 — advanced features for Casera web app

Adds sharing (residence share codes, join, user management, .casera file
export/import), subscription status with feature comparison, notification
preferences with bell icon, profile settings (edit info, change password,
theme picker, delete account), onboarding wizard with create/join paths,
enhanced dashboard with stats cards, Recharts completion chart, recent
activity feed, and task report PDF download.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-03 09:31:29 -06:00
commit 5a50d77515
183 changed files with 34450 additions and 0 deletions
@@ -0,0 +1,75 @@
"use client";
import Link from "next/link";
import { Phone, Mail, Star } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardAction } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { ContractorResponse } from "@/lib/api/contractors";
interface ContractorCardProps {
contractor: ContractorResponse;
onToggleFavorite: (id: number) => void;
}
export function ContractorCard({ contractor, onToggleFavorite }: ContractorCardProps) {
return (
<Card className="transition-shadow hover:shadow-md">
<CardHeader>
<Link href={`/app/contractors/${contractor.id}`} className="hover:underline">
<CardTitle>{contractor.name}</CardTitle>
</Link>
{contractor.company && (
<CardDescription>{contractor.company}</CardDescription>
)}
<CardAction>
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={(e) => {
e.preventDefault();
onToggleFavorite(contractor.id);
}}
>
<Star
className={
contractor.is_favorite
? "size-4 fill-yellow-400 text-yellow-400"
: "size-4 text-muted-foreground"
}
/>
</Button>
</CardAction>
</CardHeader>
<CardContent>
{contractor.specialties.length > 0 && (
<div className="flex flex-wrap gap-1 mb-3">
{contractor.specialties.map((s) => (
<Badge key={s.id} variant="secondary">
{s.icon && <span className="mr-1">{s.icon}</span>}
{s.name}
</Badge>
))}
</div>
)}
<div className="flex items-center gap-2">
{contractor.phone && (
<Button variant="outline" size="icon" className="size-8" asChild>
<a href={`tel:${contractor.phone}`} onClick={(e) => e.stopPropagation()}>
<Phone className="size-4" />
</a>
</Button>
)}
{contractor.email && (
<Button variant="outline" size="icon" className="size-8" asChild>
<a href={`mailto:${contractor.email}`} onClick={(e) => e.stopPropagation()}>
<Mail className="size-4" />
</a>
</Button>
)}
</div>
</CardContent>
</Card>
);
}