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
+111
View File
@@ -0,0 +1,111 @@
"use client";
import { useDroppable } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { TaskCard } from "./task-card";
import type { KanbanColumn as KanbanColumnType } from "@/lib/api/tasks";
const COLUMN_COLORS: Record<string, string> = {
overdue: "border-red-500/50 bg-red-50/50 dark:bg-red-950/20",
due_today: "border-orange-500/50 bg-orange-50/50 dark:bg-orange-950/20",
due_soon: "border-yellow-500/50 bg-yellow-50/50 dark:bg-yellow-950/20",
upcoming: "border-blue-500/50 bg-blue-50/50 dark:bg-blue-950/20",
in_progress: "border-green-500/50 bg-green-50/50 dark:bg-green-950/20",
completed: "border-gray-500/50 bg-gray-50/50 dark:bg-gray-950/20",
};
const COLUMN_HEADER_COLORS: Record<string, string> = {
overdue: "text-red-700 dark:text-red-400",
due_today: "text-orange-700 dark:text-orange-400",
due_soon: "text-yellow-700 dark:text-yellow-400",
upcoming: "text-blue-700 dark:text-blue-400",
in_progress: "text-green-700 dark:text-green-400",
completed: "text-gray-700 dark:text-gray-400",
};
const COUNT_BADGE_COLORS: Record<string, string> = {
overdue: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
due_today: "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200",
due_soon: "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200",
upcoming: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
in_progress: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
completed: "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200",
};
interface KanbanColumnProps {
column: KanbanColumnType;
}
function SortableTask({ task }: { task: import("@/lib/api/tasks").TaskResponse }) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: task.id });
const style: React.CSSProperties = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
<TaskCard task={task} isDragging={isDragging} />
</div>
);
}
export function KanbanColumn({ column }: KanbanColumnProps) {
const { setNodeRef, isOver } = useDroppable({
id: column.name,
});
const taskIds = column.tasks.map((t) => t.id);
return (
<div
className={cn(
"flex flex-col min-w-[280px] max-w-[320px] rounded-lg border-2 p-3",
COLUMN_COLORS[column.name] ?? "border-border bg-muted/30",
isOver && "ring-2 ring-primary"
)}
>
<div className="flex items-center gap-2 mb-3">
<h3
className={cn(
"font-semibold text-sm",
COLUMN_HEADER_COLORS[column.name]
)}
>
{column.display_name}
</h3>
<Badge
variant="secondary"
className={cn("text-xs", COUNT_BADGE_COLORS[column.name])}
>
{column.count}
</Badge>
</div>
<div ref={setNodeRef} className="flex-1 space-y-2 min-h-[60px]">
<SortableContext items={taskIds} strategy={verticalListSortingStrategy}>
{column.tasks.map((task) => (
<SortableTask key={task.id} task={task} />
))}
</SortableContext>
{column.tasks.length === 0 && (
<div className="flex items-center justify-center h-[60px] text-xs text-muted-foreground rounded-md border border-dashed">
No tasks
</div>
)}
</div>
</div>
);
}