feat(workspace): selector de área en GlobalSettingsPanel reemplaza campo libre "Cliente"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 21:06:39 -03:00
parent de00e3a902
commit e0b4607603

View File

@@ -11,6 +11,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { useProcessStore } from '@/store/process-store' import { useProcessStore } from '@/store/process-store'
import { useAuth } from '@/auth/AuthContext' import { useAuth } from '@/auth/AuthContext'
import { formatPercent } from '@/lib/format' import { formatPercent } from '@/lib/format'
import { supabaseAreaRepo } from '@/persistence/supabase/area-repo'
import type { Area } from '@/domain/types'
const CURRENCIES = [ const CURRENCIES = [
{ code: 'USD', label: 'USD — Dólar estadounidense' }, { code: 'USD', label: 'USD — Dólar estadounidense' },
@@ -28,7 +30,9 @@ export function GlobalSettingsPanel() {
const { user } = useAuth() const { user } = useAuth()
const isClientRole = user?.platformRole === 'client_editor' || user?.platformRole === 'client_viewer' const isClientRole = user?.platformRole === 'client_editor' || user?.platformRole === 'client_viewer'
const [localName, setLocalName] = useState('') const [localName, setLocalName] = useState('')
const [localClient, setLocalClient] = useState('') const [localAreaId, setLocalAreaId] = useState<string>('')
const [areas, setAreas] = useState<Area[]>([])
const [areasLoading, setAreasLoading] = useState(false)
const [localCurrency, setLocalCurrency] = useState('USD') const [localCurrency, setLocalCurrency] = useState('USD')
const [localOverhead, setLocalOverhead] = useState(20) const [localOverhead, setLocalOverhead] = useState(20)
const [localFrequency, setLocalFrequency] = useState(1000) const [localFrequency, setLocalFrequency] = useState(1000)
@@ -39,7 +43,18 @@ export function GlobalSettingsPanel() {
useEffect(() => { useEffect(() => {
if (!currentProcess) return if (!currentProcess) return
setLocalName(currentProcess.name) setLocalName(currentProcess.name)
setLocalClient(currentProcess.clientName) setLocalAreaId(currentProcess.areaId ?? '')
// Cargar áreas de la org del proceso
if (currentProcess.orgId) {
setAreasLoading(true)
supabaseAreaRepo.getByOrg(currentProcess.orgId)
.then(setAreas)
.catch(() => setAreas([]))
.finally(() => setAreasLoading(false))
} else {
setAreas([])
}
setLocalCurrency(currentProcess.currency) setLocalCurrency(currentProcess.currency)
setLocalOverhead(Math.round(currentProcess.overheadPercentage * 100)) setLocalOverhead(Math.round(currentProcess.overheadPercentage * 100))
setLocalFrequency(currentProcess.annualFrequency) setLocalFrequency(currentProcess.annualFrequency)
@@ -54,7 +69,7 @@ export function GlobalSettingsPanel() {
const clampedHorizon = Math.min(10, Math.max(1, localHorizon)) const clampedHorizon = Math.min(10, Math.max(1, localHorizon))
await updateProcess({ await updateProcess({
name: localName.trim() || 'Proceso sin nombre', name: localName.trim() || 'Proceso sin nombre',
clientName: localClient.trim(), areaId: localAreaId === '' ? null : localAreaId,
currency: localCurrency, currency: localCurrency,
overheadPercentage: localOverhead / 100, overheadPercentage: localOverhead / 100,
annualFrequency: Math.max(1, localFrequency), annualFrequency: Math.max(1, localFrequency),
@@ -82,19 +97,35 @@ export function GlobalSettingsPanel() {
/> />
</div> </div>
{/* Cliente */} {/* Área */}
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label className="text-xs font-medium">Cliente</Label> <Label className="text-xs font-medium">Área</Label>
{isClientRole ? ( {areasLoading ? (
<p className="text-sm text-slate-600 h-8 flex items-center px-1">{localClient || '—'}</p> <div className="h-8 bg-slate-100 animate-pulse rounded-md" />
) : !currentProcess.orgId ? (
<p className="text-sm text-slate-400 h-8 flex items-center px-1 italic">Sin organización asignada</p>
) : isClientRole && user?.platformRole === 'client_viewer' ? (
<p className="text-sm text-slate-600 h-8 flex items-center px-1">{currentProcess.areaName || '—'}</p>
) : ( ) : (
<Input <Select
id="client-name" value={localAreaId}
value={localClient} onValueChange={(v) => { setLocalAreaId(v); markDirty() }}
onChange={(e) => { setLocalClient(e.target.value); markDirty() }} >
placeholder="ej. Empresa ABC" <SelectTrigger className="h-8 text-xs">
className="h-8 text-sm" <SelectValue placeholder="Sin área" />
/> </SelectTrigger>
<SelectContent>
<SelectItem value="" className="text-xs text-slate-400 italic">Sin área</SelectItem>
{areas.map((a) => (
<SelectItem key={a.id} value={a.id} className="text-xs">{a.name}</SelectItem>
))}
{areas.length === 0 && (
<div className="px-2 py-1.5 text-xs text-slate-400">
No hay áreas en esta organización
</div>
)}
</SelectContent>
</Select>
)} )}
</div> </div>