import { type Component, createMemo } from "solid-js"; import { type Task, toggleTask, calculateUrgencyFromDate, setActiveTaskId, store, copyTask } from "@/store"; import { cn } from "@/lib/utils"; import { CheckCircle2, Circle, Clock, ArrowUpCircle, Copy, Users2 } from "lucide-solid"; import { Badge } from "@/components/ui/badge"; import { For } from "solid-js"; import { useTheme } from "./ThemeProvider"; import { getThemeAdjustedColor } from "@/lib/colors"; export const TaskCard: Component<{ task: Task }> = (props) => { const { resolvedTheme } = useTheme(); const urgencyLevel = createMemo(() => calculateUrgencyFromDate(props.task.dueDate)); const urgencyColor = () => { const u = urgencyLevel(); if (u >= 8) return "text-red-500"; if (u >= 6) return "text-orange-500"; if (u >= 4) return "text-blue-500"; return "text-muted-foreground"; }; const formattedDate = () => { const date = new Date(props.task.dueDate); return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; return (
setActiveTaskId(props.task.id)} >

{props.task.title}

{props.task.content && (
{(() => { let html = props.task.content || ""; // Replace block tags with newlines first to preserve structure html = html.replace(/<\/p>/gi, '\n') .replace(/<\/div>/gi, '\n') .replace(//gi, '\n') .replace(/<\/li>/gi, '\n') .replace(/<\/h[1-6]>/gi, '\n'); const tmp = document.createElement("DIV"); tmp.innerHTML = html; const text = tmp.textContent || tmp.innerText || ""; // Replace newlines (and surrounding whitespace) with a wide gap // Using 5 non-breaking spaces worth of visual space return text.trim().replace(/\s*[\r\n]+\s*/g, ' '); })()}
)}
{/* Priority */}
P{props.task.priority}
{/* Shared Indicator - shows for individual shares OR tag-based ShareRules */} {(() => { const hasIndividualShares = props.task.sharedWith && props.task.sharedWith.length > 0; // Check if any tag-based ShareRules match this task's tags (exclude 'all' type) const hasTagRuleShares = store.shareRules.some(rule => rule.type === 'tag' && rule.tagName && props.task.tags?.includes(rule.tagName) ); const isShared = hasIndividualShares || hasTagRuleShares; if (!isShared) return null; const shareCount = (props.task.sharedWith?.length || 0); const tooltip = hasIndividualShares ? `Shared with ${shareCount} user${shareCount > 1 ? 's' : ''}` : 'Shared via tag rule'; return (
); })()} {/* Tags */} {props.task.tags && props.task.tags.length > 0 && (
{(tag) => ( { const def = store.tagDefinitions.find(d => d.name === tag); const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); return color ? `${color}15` : undefined; })(), "border-color": (() => { const def = store.tagDefinitions.find(d => d.name === tag); const color = getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()); return color ? `${color}30` : undefined; })(), "color": (() => { const def = store.tagDefinitions.find(d => d.name === tag); return getThemeAdjustedColor(def?.color, def?.theme, resolvedTheme()) || "inherit"; })() }} >
d.name === tag)?.value ?? 5) > 5 ? "#22c55e" : (store.tagDefinitions.find(d => d.name === tag)?.value ?? 5) < 5 ? "#ef4444" : "#94a3b8" }} /> {tag} )}
)} {/* Due Date + Urgency Slide-out */}
{/* Due Date - Static Layer on Top */}
{formattedDate()}
{/* Urgency Number - Slides out from behind with fade */}
Urgency {urgencyLevel()}
); };