Files
EstiMaker/src/components/estimate-builder/ScopesList.tsx
T

129 lines
6.7 KiB
TypeScript

import type { Component } from 'solid-js';
import { For, Show } from 'solid-js';
import { FolderKanban, Plus, FileText, ChevronDown } from 'lucide-solid';
import ScopeCard from '../ScopeCard';
import NoteEditor from '../NoteEditor';
import type { Scope, SubScope, EstimateItem } from '../../types';
interface ScopesListProps {
scopes: Scope[];
subScopes: SubScope[];
items: EstimateItem[];
isNotesExpanded: boolean;
setIsNotesExpanded: (expanded: boolean) => void;
generalEstimateNotes: string;
setGeneralEstimateNotes: (notes: string) => void;
updateScope: (id: string, updates: Partial<Scope>) => void;
deleteScope: (id: string) => void;
updateItem: (id: string, updates: Partial<EstimateItem>) => void;
deleteItem: (id: string) => void;
addSubScope: (scopeId: string) => void;
updateSubScope: (id: string, updates: Partial<SubScope>) => void;
deleteSubScope: (id: string) => void;
handleDragStart: (e: DragEvent, id: string) => void;
handleDrop: (e: DragEvent, scopeId?: string, subScopeId?: string) => void;
handleBulkUpdate: (scopeId: string, subScopeId: string | undefined, updates: any) => void;
duplicateItem: (id: string) => void;
copySubScopeItems: (sourceSubScopeId: string, targetSubScopeId: string) => void;
setApplyTemplateScopeId: (id: string | null) => void;
isSidebarCollapsed: () => boolean;
selectedIds: string[];
toggleSelection: (id: string, isMulti: boolean, isShift: boolean) => void;
clearSelection: (e?: MouseEvent) => void;
anyDescriptionIsLong: () => boolean;
addScope: () => void;
}
export const ScopesList: Component<ScopesListProps> = (props) => {
return (
<div class="space-y-12">
<For each={props.scopes}>
{(scope) => (
<div id={`scope-${scope.id}`}>
<ScopeCard
scope={scope}
subScopes={props.subScopes.filter(ss => ss.scopeId === scope.id)}
items={props.items.filter(i => i.scopeId === scope.id)}
onUpdateScope={props.updateScope}
onDeleteScope={props.deleteScope}
onUpdateItem={props.updateItem}
onDeleteItem={props.deleteItem}
onAddSubScope={props.addSubScope}
onUpdateSubScope={props.updateSubScope}
onDeleteSubScope={props.deleteSubScope}
onDragStart={props.handleDragStart}
onDrop={props.handleDrop}
onBulkUpdate={props.handleBulkUpdate}
onDuplicateItem={props.duplicateItem}
allScopes={props.scopes}
allSubScopes={props.subScopes}
onCopySubScopeItems={props.copySubScopeItems}
onOpenApplyTemplate={props.setApplyTemplateScopeId}
isSidebarCollapsed={props.isSidebarCollapsed}
selectedIds={props.selectedIds}
onToggleSelection={props.toggleSelection}
onClearSelection={props.clearSelection}
hideColumns={props.anyDescriptionIsLong()}
/>
</div>
)}
</For>
<Show when={props.scopes.length > 0}>
<div class="mt-8 bg-card border border-border rounded-3xl overflow-hidden shadow-premium transition-all duration-300">
<button
onClick={() => props.setIsNotesExpanded(!props.isNotesExpanded)}
class="w-full flex items-center justify-between p-6 hover:bg-muted/30 transition-colors"
>
<div class="flex items-center gap-3 text-foreground">
<div class="p-2 bg-muted rounded-xl shadow-sm">
<FileText class="w-5 h-5 text-muted-foreground" />
</div>
<div class="text-left">
<h3 class="font-black text-lg">General Estimate Notes</h3>
<p class="text-muted-foreground text-xs font-bold uppercase tracking-wider">Configure standard project terms and exclusions</p>
</div>
</div>
<div class={`transition-transform duration-300 ${props.isNotesExpanded ? 'rotate-180' : ''}`}>
<ChevronDown class="w-5 h-5 text-muted-foreground" />
</div>
</button>
<div class={`transition-all duration-300 ease-in-out ${props.isNotesExpanded ? 'max-h-[2000px] border-t border-gray-50' : 'max-h-0'}`}>
<div class="p-6 pt-2">
<NoteEditor
label="Inclusions, Exclusions & Terms"
value={props.generalEstimateNotes}
onUpdate={props.setGeneralEstimateNotes}
icon={FileText}
placeholder="Add project-wide notes here..."
highlight={props.generalEstimateNotes.includes('#')}
/>
</div>
</div>
</div>
</Show>
<Show when={props.scopes.length === 0}>
<div class="flex flex-col items-center justify-center py-20 text-center space-y-6">
<div class="p-8 bg-muted rounded-full text-muted-foreground/30 shadow-inner">
<FolderKanban class="w-16 h-16" />
</div>
<div class="space-y-2">
<h3 class="text-2xl font-black text-foreground tracking-tight">No Scopes Defined</h3>
<p class="text-muted-foreground max-w-sm mx-auto font-medium">Click "New Scope" in the header to start building your estimate structure.</p>
</div>
<button
onClick={props.addScope}
class="flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground rounded-2xl font-black text-xs uppercase tracking-widest hover:bg-primary/90 transition-all shadow-xl shadow-primary/20 active:scale-95"
>
<Plus class="w-5 h-5" /> Start First Scope
</button>
</div>
</Show>
</div>
);
};