basic module setup

This commit is contained in:
2026-03-16 13:38:22 -05:00
parent f4ad67e097
commit 865c4a954c
10 changed files with 373 additions and 14 deletions
+106
View File
@@ -0,0 +1,106 @@
import type { Component } from 'solid-js';
import { Show } from 'solid-js';
import { Trash2, Calculator, EyeOff } from 'lucide-solid';
import NumericInput from './ui/NumericInput';
import type { Modifier } from '../types';
import { ModifierRegistry } from '../utils/modifier-registry';
interface ModifierRowProps {
modifier: Modifier;
calculatedValue: number;
onUpdate: (id: string, updates: Partial<Modifier>) => void;
onDelete: (id: string) => void;
}
const ModifierRow: Component<ModifierRowProps> = (props) => {
const module = () => ModifierRegistry.getModule(props.modifier.moduleId);
const formatNumber = (num: number) => {
return num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};
return (
<div class="group flex items-center justify-between py-2 px-1 hover:bg-primary/5 rounded-xl transition-all">
<div class="flex items-center gap-3">
{/* Name */}
<input
type="text"
title={module().description}
value={props.modifier.name}
onInput={(e) => props.onUpdate(props.modifier.id, { name: e.currentTarget.value })}
class="bg-transparent border-none outline-none text-sm font-medium text-muted-foreground focus:text-foreground focus:ring-0 py-0.5 w-48"
placeholder="Modifier Name"
/>
{/* Type Toggles - Visible on hover */}
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => props.onUpdate(props.modifier.id, { valueType: 'percent' })}
class={`p-1 rounded-md transition-colors text-[10px] font-bold ${props.modifier.valueType === 'percent' ? 'bg-primary/10 text-primary' : 'hover:bg-muted text-muted-foreground'}`}
>
%
</button>
<button
onClick={() => props.onUpdate(props.modifier.id, { valueType: 'amount' })}
class={`p-1 rounded-md transition-colors text-[10px] font-bold ${props.modifier.valueType === 'amount' ? 'bg-primary/10 text-primary' : 'hover:bg-muted text-muted-foreground'}`}
>
$
</button>
<button
onClick={() => {
const unit = props.modifier.unitLabel || 'hrs';
props.onUpdate(props.modifier.id, { valueType: 'unit', unitLabel: unit });
}}
class={`px-1.5 py-1 rounded-md transition-colors text-[9px] font-black tracking-tighter ${props.modifier.valueType === 'unit' ? 'bg-primary/10 text-primary' : 'hover:bg-muted text-muted-foreground'}`}
>
UNIT
</button>
</div>
</div>
<div class="flex items-center gap-4">
{/* Value Input */}
<div class="flex items-center gap-2">
<NumericInput
value={props.modifier.value}
onUpdate={(val) => props.onUpdate(props.modifier.id, { value: val })}
class="w-20 text-right text-sm border-b border-dashed border-border focus:border-primary outline-none bg-transparent text-primary font-medium"
placeholder="0.00"
/>
<Show when={props.modifier.valueType === 'unit' && props.modifier.unitLabel}>
<span class="text-[10px] font-bold text-primary/60">{props.modifier.unitLabel}</span>
</Show>
</div>
{/* Calculated Result */}
<div class="w-28 text-right flex items-center justify-end gap-1.5">
<span class={`text-sm font-medium transition-colors ${props.modifier.includeInTotal ? 'text-foreground' : 'text-muted-foreground/40'}`}>
{props.modifier.moduleId === 'man-hours' ? '' : '$'}{formatNumber(props.calculatedValue)}
{props.modifier.moduleId === 'man-hours' ? ' hrs' : ''}
</span>
{/* Active/Draft Toggle */}
<button
onClick={() => props.onUpdate(props.modifier.id, { includeInTotal: !props.modifier.includeInTotal })}
class={`p-1 rounded-md transition-all ${props.modifier.includeInTotal ? 'text-primary' : 'text-muted-foreground/30'}`}
title={props.modifier.includeInTotal ? 'Included' : 'Excluded'}
>
<Show when={props.modifier.includeInTotal} fallback={<EyeOff class="w-3.5 h-3.5" />}>
<Calculator class="w-3.5 h-3.5" />
</Show>
</button>
</div>
{/* Delete */}
<button
onClick={() => props.onDelete(props.modifier.id)}
class="p-1 opacity-0 group-hover:opacity-100 text-muted-foreground/30 hover:text-destructive hover:bg-destructive/10 rounded-lg transition-all"
>
<Trash2 class="w-3.5 h-3.5" />
</button>
</div>
</div>
);
};
export default ModifierRow;