86 lines
4.1 KiB
TypeScript
86 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import type { Schedule } from "@/lib/shared/types";
|
|
|
|
type SchedulesListProps = {
|
|
activeGroupId: number | null;
|
|
loading: boolean;
|
|
schedules: Schedule[];
|
|
visibleSchedules: Schedule[];
|
|
activeFilterCount: number;
|
|
onOpenDetails: (schedule: Schedule, index: number) => void;
|
|
onClearFilters: () => void;
|
|
};
|
|
|
|
export default function SchedulesList({
|
|
activeGroupId,
|
|
loading,
|
|
schedules,
|
|
visibleSchedules,
|
|
activeFilterCount,
|
|
onOpenDetails,
|
|
onClearFilters
|
|
}: SchedulesListProps) {
|
|
return (
|
|
<div className="mt-3 space-y-2">
|
|
{!activeGroupId ? (
|
|
<div className="text-sm text-muted">Select a group to view schedules.</div>
|
|
) : loading ? (
|
|
<div className="space-y-2">
|
|
{[0, 1, 2].map(row => (
|
|
<div key={row} className="rounded-lg border border-accent-weak bg-panel px-3 py-3">
|
|
<div className="animate-pulse space-y-2">
|
|
<div className="h-4 w-28 rounded bg-surface" />
|
|
<div className="h-3 w-40 rounded bg-surface" />
|
|
<div className="h-3 w-36 rounded bg-surface" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : schedules.length ? (
|
|
visibleSchedules.length ? (
|
|
visibleSchedules.map((schedule, index) => (
|
|
<div
|
|
key={schedule.id}
|
|
className="flex min-h-[84px] cursor-pointer flex-col justify-between gap-2 rounded-lg border border-accent-weak bg-panel px-3 py-2 text-sm transition hover:border-accent hover:bg-accent-soft"
|
|
onClick={() => onOpenDetails(schedule, index)}
|
|
>
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div className="flex items-center gap-2">
|
|
<div className="text-base font-semibold">${schedule.amountDollars.toFixed(2)}</div>
|
|
<div className={`rounded-full border px-2 py-0.5 text-[10px] ${schedule.isActive ? "border-green-400/60 bg-green-500/10 text-green-200" : "border-amber-400/60 bg-amber-500/10 text-amber-200"}`}>
|
|
{schedule.isActive ? "Active" : "Paused"}
|
|
</div>
|
|
</div>
|
|
<div className="text-xs text-muted">Next: {schedule.nextRunOn}</div>
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-1.5">
|
|
{(schedule.tags || []).length ? (
|
|
schedule.tags.map(tag => (
|
|
<span key={`${schedule.id}-${tag}`} className="rounded-full border border-accent-weak bg-accent-soft px-2 py-0.5 text-xs">
|
|
#{tag}
|
|
</span>
|
|
))
|
|
) : (
|
|
<span className="rounded-full border border-accent-weak px-2 py-0.5 text-xs text-soft">No tags</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="space-y-2 text-sm text-muted">
|
|
<div>No matching schedules.</div>
|
|
{activeFilterCount ? (
|
|
<button type="button" className="rounded-lg btn-outline-accent px-3 py-1 text-xs" onClick={onClearFilters}>
|
|
Clear filters
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
)
|
|
) : (
|
|
<div className="text-sm text-muted">No schedules yet.</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|