Critical PDA-friendly design compliance fix.
Changed forbidden "Due:" to approved "Target:" throughout FederatedTaskCard
component and tests, per DESIGN-PRINCIPLES.md requirements.
Changes:
- FederatedTaskCard.tsx: Changed "Due: {dueDate}" to "Target: {dueDate}"
- FederatedTaskCard.test.tsx: Updated all test expectations from "Due:" to "Target:"
- Updated test names to reflect "target date" terminology
All 11 tests passing.
This ensures full compliance with PDA-friendly language guidelines:
| ❌ NEVER | ✅ ALWAYS |
| DUE | Target date |
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
/**
|
|
* FederatedTaskCard Component
|
|
* Displays a task from a federated instance with provenance indicator
|
|
*/
|
|
|
|
import { TaskStatus, TaskPriority } from "@mosaic/shared";
|
|
import type { FederatedTask } from "./types";
|
|
import { ProvenanceIndicator } from "./ProvenanceIndicator";
|
|
|
|
interface FederatedTaskCardProps {
|
|
federatedTask: FederatedTask;
|
|
compact?: boolean;
|
|
onClick?: () => void;
|
|
}
|
|
|
|
/**
|
|
* Get PDA-friendly status text and color
|
|
*/
|
|
function getStatusDisplay(status: TaskStatus): { text: string; colorClass: string } {
|
|
switch (status) {
|
|
case TaskStatus.NOT_STARTED:
|
|
return { text: "Not Started", colorClass: "bg-gray-100 text-gray-700" };
|
|
case TaskStatus.IN_PROGRESS:
|
|
return { text: "In Progress", colorClass: "bg-blue-100 text-blue-700" };
|
|
case TaskStatus.COMPLETED:
|
|
return { text: "Completed", colorClass: "bg-green-100 text-green-700" };
|
|
case TaskStatus.PAUSED:
|
|
return { text: "Paused", colorClass: "bg-yellow-100 text-yellow-700" };
|
|
case TaskStatus.ARCHIVED:
|
|
return { text: "Archived", colorClass: "bg-gray-100 text-gray-600" };
|
|
default:
|
|
return { text: "Unknown", colorClass: "bg-gray-100 text-gray-700" };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get priority text and color
|
|
*/
|
|
function getPriorityDisplay(priority: TaskPriority): { text: string; colorClass: string } {
|
|
switch (priority) {
|
|
case TaskPriority.LOW:
|
|
return { text: "Low", colorClass: "text-gray-600" };
|
|
case TaskPriority.MEDIUM:
|
|
return { text: "Medium", colorClass: "text-blue-600" };
|
|
case TaskPriority.HIGH:
|
|
return { text: "High", colorClass: "text-orange-600" };
|
|
default:
|
|
return { text: "Unknown", colorClass: "text-gray-600" };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format date for display
|
|
*/
|
|
function formatDate(date: Date | null): string | null {
|
|
if (!date) {
|
|
return null;
|
|
}
|
|
return new Intl.DateTimeFormat("en-US", {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric",
|
|
}).format(new Date(date));
|
|
}
|
|
|
|
export function FederatedTaskCard({
|
|
federatedTask,
|
|
compact = false,
|
|
onClick,
|
|
}: FederatedTaskCardProps): React.JSX.Element {
|
|
const { task, provenance } = federatedTask;
|
|
const status = getStatusDisplay(task.status);
|
|
const priority = getPriorityDisplay(task.priority);
|
|
const dueDate = formatDate(task.dueDate);
|
|
|
|
const paddingClass = compact ? "p-3" : "p-4";
|
|
const clickableClass = onClick ? "cursor-pointer hover:border-gray-300" : "";
|
|
|
|
return (
|
|
<div
|
|
className={`border border-gray-200 rounded-lg ${paddingClass} ${clickableClass} transition-colors`}
|
|
onClick={onClick}
|
|
>
|
|
{/* Header with title and provenance */}
|
|
<div className="flex items-start justify-between mb-2">
|
|
<div className="flex-1">
|
|
<h3 className="text-base font-medium text-gray-900">{task.title}</h3>
|
|
{task.description && <p className="text-sm text-gray-600 mt-1">{task.description}</p>}
|
|
</div>
|
|
<ProvenanceIndicator
|
|
instanceId={provenance.instanceId}
|
|
instanceName={provenance.instanceName}
|
|
instanceUrl={provenance.instanceUrl}
|
|
compact={compact}
|
|
/>
|
|
</div>
|
|
|
|
{/* Metadata row */}
|
|
<div className="flex items-center gap-3 text-sm flex-wrap">
|
|
{/* Status badge */}
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${status.colorClass}`}>
|
|
{status.text}
|
|
</span>
|
|
|
|
{/* Priority */}
|
|
<span className={`text-xs font-medium ${priority.colorClass}`}>{priority.text}</span>
|
|
|
|
{/* Target date */}
|
|
{dueDate && <span className="text-xs text-gray-600">Target: {dueDate}</span>}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|