feat(captura): fechas de Obra bloqueadas tras establecerse, solo admin las cambia
This commit is contained in:
@@ -277,4 +277,46 @@ describe('TablaCaptura', () => {
|
||||
expect(onGuardar).toHaveBeenCalledTimes(1)
|
||||
expect(onGuardar.mock.calls[0][0]['409-1']).toEqual({ comentarios_obra: 'Nota X' })
|
||||
})
|
||||
|
||||
it('fechas de Obra guardadas: disabled para no-admin, editables para admin', () => {
|
||||
const camposEmparejados: CampoDef[] = [
|
||||
{ campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas', conFecha: 'certificacion_gas_fecha' },
|
||||
{ campo: 'certificacion_gas_fecha', tipo: 'date', label: 'Fecha Certificación Gas' },
|
||||
]
|
||||
const datosConFecha: DatosCaptura = {
|
||||
'409-1': { certificacion_gas: null, certificacion_gas_fecha: '2026-01-01' },
|
||||
'409-2': { certificacion_gas: null, certificacion_gas_fecha: null },
|
||||
}
|
||||
|
||||
// No-admin: la fecha guardada se deshabilita, la vacía sigue editable
|
||||
const r1 = render(
|
||||
<TablaCaptura
|
||||
unidades={UNIDADES}
|
||||
campos={camposEmparejados}
|
||||
datos={datosConFecha}
|
||||
onGuardar={vi.fn()}
|
||||
guardando={false}
|
||||
puedeEditarFechas={false}
|
||||
/>,
|
||||
)
|
||||
const fechas1 = r1.container.querySelectorAll('input[type="date"]')
|
||||
expect((fechas1[0] as HTMLInputElement).disabled).toBe(true)
|
||||
expect((fechas1[1] as HTMLInputElement).disabled).toBe(false)
|
||||
r1.unmount()
|
||||
|
||||
// Admin: todas editables
|
||||
const r2 = render(
|
||||
<TablaCaptura
|
||||
unidades={UNIDADES}
|
||||
campos={camposEmparejados}
|
||||
datos={datosConFecha}
|
||||
onGuardar={vi.fn()}
|
||||
guardando={false}
|
||||
puedeEditarFechas
|
||||
/>,
|
||||
)
|
||||
const fechas2 = r2.container.querySelectorAll('input[type="date"]')
|
||||
expect((fechas2[0] as HTMLInputElement).disabled).toBe(false)
|
||||
expect((fechas2[1] as HTMLInputElement).disabled).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,6 +13,8 @@ interface Props {
|
||||
guardando: boolean
|
||||
/** Filtro de estatus de unidad (columna estestatus en VENTAS) */
|
||||
filtroEstatus?: string
|
||||
/** Si es false, las fechas de Obra ya guardadas no se pueden editar (solo admin) */
|
||||
puedeEditarFechas?: boolean
|
||||
}
|
||||
|
||||
const POR_PAGINA = 50
|
||||
@@ -57,6 +59,8 @@ interface CeldaProps {
|
||||
valorFecha?: string | null
|
||||
dirtyFecha?: boolean
|
||||
onFechaChange?: (nuevo: string | null) => void
|
||||
/** Fecha ya establecida y usuario sin permiso de edición */
|
||||
fechaBloqueada?: boolean
|
||||
}
|
||||
|
||||
function CeldaEditable({
|
||||
@@ -69,6 +73,7 @@ function CeldaEditable({
|
||||
valorFecha,
|
||||
dirtyFecha,
|
||||
onFechaChange,
|
||||
fechaBloqueada,
|
||||
}: CeldaProps) {
|
||||
const bordeDirty = dirty ? 'ring-2 ring-accent/40' : ''
|
||||
|
||||
@@ -88,8 +93,14 @@ function CeldaEditable({
|
||||
<input
|
||||
type="date"
|
||||
value={valorFecha ?? ''}
|
||||
disabled={fechaBloqueada}
|
||||
onChange={(e) => onFechaChange?.(e.target.value || null)}
|
||||
className={`w-28 border-0 bg-transparent px-1 py-0.5 text-xs focus:outline-none [color-scheme:light] dark:[color-scheme:dark] ${colorFecha} ${bordeFecha}`}
|
||||
title={
|
||||
fechaBloqueada
|
||||
? 'Fecha ya establecida — solo un administrador puede modificarla'
|
||||
: undefined
|
||||
}
|
||||
className={`w-28 border-0 bg-transparent px-1 py-0.5 text-xs focus:outline-none [color-scheme:light] dark:[color-scheme:dark] disabled:cursor-not-allowed ${colorFecha} ${bordeFecha}`}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
@@ -159,6 +170,7 @@ export function TablaCaptura({
|
||||
onGuardar,
|
||||
guardando,
|
||||
filtroEstatus,
|
||||
puedeEditarFechas = true,
|
||||
}: Props) {
|
||||
const [dirty, setDirty] = useState<DirtyMap>({})
|
||||
const [pagina, setPagina] = useState(0)
|
||||
@@ -314,6 +326,12 @@ export function TablaCaptura({
|
||||
campoFecha ? valorEfectivo(dirty, datos, unidad, campoFecha) : undefined
|
||||
}
|
||||
dirtyFecha={campoFecha ? dirty[unidad]?.[campoFecha] !== undefined : false}
|
||||
fechaBloqueada={
|
||||
campoFecha
|
||||
? !puedeEditarFechas &&
|
||||
valorOriginal(datos, unidad, campoFecha) != null
|
||||
: false
|
||||
}
|
||||
onFechaChange={
|
||||
campoFecha
|
||||
? (nuevo) => handleChange(unidad, campoFecha, nuevo)
|
||||
|
||||
@@ -87,8 +87,12 @@ export function CapturaPage({ onVolver }: Props) {
|
||||
await guardarBulk({ desarrollo, departamento, registros })
|
||||
toast.success('Cambios guardados')
|
||||
await cargar()
|
||||
} catch {
|
||||
toast.error('No se pudieron guardar los cambios')
|
||||
} catch (e) {
|
||||
toast.error(
|
||||
e instanceof Error && e.message && !e.message.startsWith('HTTP')
|
||||
? e.message
|
||||
: 'No se pudieron guardar los cambios',
|
||||
)
|
||||
} finally {
|
||||
setGuardando(false)
|
||||
}
|
||||
@@ -215,6 +219,7 @@ export function CapturaPage({ onVolver }: Props) {
|
||||
onGuardar={handleGuardar}
|
||||
guardando={guardando}
|
||||
filtroEstatus={filtroEstatus}
|
||||
puedeEditarFechas={rol === 'admin'}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -161,7 +161,10 @@ export async function guardarCampo(data: {
|
||||
}),
|
||||
)
|
||||
manejar401(resp)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
if (!resp.ok) {
|
||||
const cuerpo = (await resp.json().catch(() => null)) as { error?: string } | null
|
||||
throw new Error(cuerpo?.error ?? `HTTP ${resp.status}`)
|
||||
}
|
||||
}
|
||||
|
||||
/** POST /api/captura/bulk — guardar múltiples campos */
|
||||
@@ -183,5 +186,8 @@ export async function guardarBulk(data: {
|
||||
}),
|
||||
)
|
||||
manejar401(resp)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
if (!resp.ok) {
|
||||
const cuerpo = (await resp.json().catch(() => null)) as { error?: string } | null
|
||||
throw new Error(cuerpo?.error ?? `HTTP ${resp.status}`)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user