chore(repo): initialize planarchy workspace
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"use client";
|
||||
|
||||
import { clsx } from "clsx";
|
||||
import { TimelineFilter, type TimelineFilters } from "./TimelineFilter.js";
|
||||
|
||||
interface TimelineToolbarProps {
|
||||
viewMode: "resource" | "project";
|
||||
onViewModeChange: (mode: "resource" | "project") => void;
|
||||
filters: TimelineFilters;
|
||||
onFiltersChange: (f: TimelineFilters) => void;
|
||||
filterOpen: boolean;
|
||||
onFilterOpenChange: (open: boolean) => void;
|
||||
resourceCount: number;
|
||||
projectCount: number;
|
||||
totalAllocCount: number;
|
||||
onNavigateBack: () => void;
|
||||
onNavigateToday: () => void;
|
||||
onNavigateForward: () => void;
|
||||
canUndo?: boolean;
|
||||
canRedo?: boolean;
|
||||
onUndo?: () => void;
|
||||
onRedo?: () => void;
|
||||
}
|
||||
|
||||
export function TimelineToolbar({
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
filters,
|
||||
onFiltersChange,
|
||||
filterOpen,
|
||||
onFilterOpenChange,
|
||||
resourceCount,
|
||||
projectCount,
|
||||
totalAllocCount,
|
||||
onNavigateBack,
|
||||
onNavigateToday,
|
||||
onNavigateForward,
|
||||
canUndo,
|
||||
canRedo,
|
||||
onUndo,
|
||||
onRedo,
|
||||
}: TimelineToolbarProps) {
|
||||
const activeFilterCount = filters.chapters.length + filters.eids.length + filters.projectIds.length;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between mb-2 gap-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{viewMode === "resource"
|
||||
? `${resourceCount} resources · ${totalAllocCount} allocations`
|
||||
: `${projectCount} projects`}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Timeline navigation */}
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={onNavigateBack}
|
||||
className="px-2.5 py-1.5 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
title="Previous 4 weeks"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<button
|
||||
onClick={onNavigateToday}
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Today
|
||||
</button>
|
||||
<button
|
||||
onClick={onNavigateForward}
|
||||
className="px-2.5 py-1.5 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 transition-colors"
|
||||
title="Next 4 weeks"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Undo / Redo */}
|
||||
{(onUndo ?? onRedo) && (
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={onUndo}
|
||||
disabled={!canUndo}
|
||||
title="Undo (Ctrl+Z)"
|
||||
className="px-2 py-1.5 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
↩
|
||||
</button>
|
||||
<button
|
||||
onClick={onRedo}
|
||||
disabled={!canRedo}
|
||||
title="Redo (Ctrl+Shift+Z / Ctrl+Y)"
|
||||
className="px-2 py-1.5 text-sm rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
↪
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* View mode toggle */}
|
||||
<div className="flex rounded-lg border border-gray-200 overflow-hidden text-sm">
|
||||
<button
|
||||
onClick={() => onViewModeChange("resource")}
|
||||
className={clsx(
|
||||
"px-3 py-1.5 transition-colors",
|
||||
viewMode === "resource"
|
||||
? "bg-brand-600 text-white"
|
||||
: "text-gray-600 hover:bg-gray-50",
|
||||
)}
|
||||
>
|
||||
Resource view
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onViewModeChange("project")}
|
||||
className={clsx(
|
||||
"px-3 py-1.5 border-l border-gray-200 transition-colors",
|
||||
viewMode === "project"
|
||||
? "bg-brand-600 text-white"
|
||||
: "text-gray-600 hover:bg-gray-50",
|
||||
)}
|
||||
>
|
||||
Project view
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Filter */}
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => onFilterOpenChange(!filterOpen)}
|
||||
className={clsx(
|
||||
"flex items-center gap-2 px-3 py-1.5 text-sm rounded-lg border transition-colors",
|
||||
filterOpen || activeFilterCount > 0
|
||||
? "bg-brand-50 border-brand-300 text-brand-700"
|
||||
: "border-gray-200 text-gray-600 hover:bg-gray-50",
|
||||
)}
|
||||
>
|
||||
Filter
|
||||
{activeFilterCount > 0 && (
|
||||
<span className="w-4 h-4 rounded-full bg-brand-600 text-white text-xs flex items-center justify-center">
|
||||
{activeFilterCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<TimelineFilter
|
||||
filters={filters}
|
||||
onChange={onFiltersChange}
|
||||
isOpen={filterOpen}
|
||||
onClose={() => onFilterOpenChange(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user