import { type Component, createMemo, For, Show } from "solid-js";
import { type Task, calculateUrgencyFromDate, setActiveTaskId, store, copyTask, updateTask, currentTaskContext, now, getFavoriteTag, dismissTaskUpdateIndicator, isTaskUpdatedByAnotherUser, collapseTaskUntilUpdated, expandCollapsedTask, isTaskCollapsedUntilUpdated, isTaskCollaborativelyShared, getVisibleTaskTags } from "@/store";
import { cn } from "@/lib/utils";
import { Clock, ArrowUpCircle, Copy, Users2, Star, ChevronRight } from "lucide-solid";
import { Badge } from "@/components/ui/badge";
import { useTheme } from "./ThemeProvider";
import { getThemeAdjustedColor } from "@/lib/colors";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
import { getStatusOptionsForTask } from "@/lib/constants";
import { getRecurrenceProgress } from "@/lib/recurrence";
import { StatusCircle } from "./StatusCircle";
export const TaskCard: Component<{ task: Task, isShaking?: boolean }> = (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' });
};
const visibleTags = createMemo(() => {
return getVisibleTaskTags(props.task, currentTaskContext());
});
const isDueWithin12Hours = createMemo(() => {
if (!props.task.dueDate) return false;
try {
const dueMs = new Date(props.task.dueDate).getTime();
const diffMs = dueMs - now();
const twelveHoursMs = 12 * 60 * 60 * 1000;
return Math.abs(diffMs) <= twelveHoursMs;
} catch {
return false;
}
});
const canCollapseUntilUpdated = createMemo(() => isTaskCollaborativelyShared(props.task));
const statusOptions = createMemo(() => {
const baseOptions = getStatusOptionsForTask(props.task.recurrence);
if (!canCollapseUntilUpdated()) return baseOptions;
return [
...baseOptions,
{ value: "__collapse_until_updated__", label: "Collapse until updated" }
];
});
const recurrenceProgress = createMemo(() => {
if (!props.task.completed && props.task.status !== 10) return null;
return getRecurrenceProgress(props.task.recurrence, props.task.updated, now());
});
const shouldQuickloadFade = createMemo(() => store.quickloadFadeTaskIds.includes(props.task.id));
const showUpdateIndicator = createMemo(() => isTaskUpdatedByAnotherUser(props.task));
const isCollapsed = createMemo(() => isTaskCollapsedUntilUpdated(props.task));
return (
setActiveTaskId(props.task.id)}
>
{/* Today indicator */}
{!isCollapsed() && "Today"}
{/* Status Dropdown */}
e.stopPropagation()}>
{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 */}
{/* Shared Indicator - driven by task context tags */}
{(() => {
if (props.task.shareRefs.length === 0) return null;
const tooltip = props.task.shareRefs.length === 1
? 'Shared via 1 context'
: `Shared via ${props.task.shareRefs.length} contexts`;
return (
);
})()}
{/* Tags */}
{visibleTags().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()}
>
}
>
{props.task.title}
);
};