300 lines
9.8 KiB
TypeScript
300 lines
9.8 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
||
|
|
import { Settings, Save, RotateCcw, AlertCircle, Loader } from 'lucide-react';
|
||
|
|
import { Button, Card } from '../../../../components/ui';
|
||
|
|
import { PageHeader } from '../../../../components/layout';
|
||
|
|
import { useToast } from '../../../../hooks/ui/useToast';
|
||
|
|
import { useSettings, useUpdateSettings } from '../../../../api/hooks/settings';
|
||
|
|
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||
|
|
import type {
|
||
|
|
TenantSettings,
|
||
|
|
ProcurementSettings,
|
||
|
|
InventorySettings,
|
||
|
|
ProductionSettings,
|
||
|
|
SupplierSettings,
|
||
|
|
POSSettings,
|
||
|
|
OrderSettings,
|
||
|
|
} from '../../../../api/types/settings';
|
||
|
|
import ProcurementSettingsCard from './cards/ProcurementSettingsCard';
|
||
|
|
import InventorySettingsCard from './cards/InventorySettingsCard';
|
||
|
|
import ProductionSettingsCard from './cards/ProductionSettingsCard';
|
||
|
|
import SupplierSettingsCard from './cards/SupplierSettingsCard';
|
||
|
|
import POSSettingsCard from './cards/POSSettingsCard';
|
||
|
|
import OrderSettingsCard from './cards/OrderSettingsCard';
|
||
|
|
|
||
|
|
const AjustesPage: React.FC = () => {
|
||
|
|
const { addToast } = useToast();
|
||
|
|
const currentTenant = useCurrentTenant();
|
||
|
|
const tenantId = currentTenant?.id || '';
|
||
|
|
|
||
|
|
const { data: settings, isLoading, error, isFetching } = useSettings(tenantId, {
|
||
|
|
enabled: !!tenantId,
|
||
|
|
retry: 2,
|
||
|
|
staleTime: 5 * 60 * 100,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Debug logging
|
||
|
|
React.useEffect(() => {
|
||
|
|
console.log('🔍 AjustesPage - tenantId:', tenantId);
|
||
|
|
console.log('🔍 AjustesPage - settings:', settings);
|
||
|
|
console.log('🔍 AjustesPage - isLoading:', isLoading);
|
||
|
|
console.log('🔍 AjustesPage - isFetching:', isFetching);
|
||
|
|
console.log('🔍 AjustesPage - error:', error);
|
||
|
|
}, [tenantId, settings, isLoading, isFetching, error]);
|
||
|
|
const updateSettingsMutation = useUpdateSettings();
|
||
|
|
|
||
|
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||
|
|
const [isSaving, setIsSaving] = useState(false);
|
||
|
|
|
||
|
|
// Local state for each category
|
||
|
|
const [procurementSettings, setProcurementSettings] = useState<ProcurementSettings | null>(null);
|
||
|
|
const [inventorySettings, setInventorySettings] = useState<InventorySettings | null>(null);
|
||
|
|
const [productionSettings, setProductionSettings] = useState<ProductionSettings | null>(null);
|
||
|
|
const [supplierSettings, setSupplierSettings] = useState<SupplierSettings | null>(null);
|
||
|
|
const [posSettings, setPosSettings] = useState<POSSettings | null>(null);
|
||
|
|
const [orderSettings, setOrderSettings] = useState<OrderSettings | null>(null);
|
||
|
|
|
||
|
|
// Load settings into local state when data is fetched
|
||
|
|
React.useEffect(() => {
|
||
|
|
if (settings) {
|
||
|
|
setProcurementSettings(settings.procurement_settings);
|
||
|
|
setInventorySettings(settings.inventory_settings);
|
||
|
|
setProductionSettings(settings.production_settings);
|
||
|
|
setSupplierSettings(settings.supplier_settings);
|
||
|
|
setPosSettings(settings.pos_settings);
|
||
|
|
setOrderSettings(settings.order_settings);
|
||
|
|
setHasUnsavedChanges(false);
|
||
|
|
}
|
||
|
|
}, [settings]);
|
||
|
|
|
||
|
|
const handleSaveAll = async () => {
|
||
|
|
if (!tenantId || !procurementSettings || !inventorySettings || !productionSettings ||
|
||
|
|
!supplierSettings || !posSettings || !orderSettings) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
setIsSaving(true);
|
||
|
|
|
||
|
|
try {
|
||
|
|
await updateSettingsMutation.mutateAsync({
|
||
|
|
tenantId,
|
||
|
|
updates: {
|
||
|
|
procurement_settings: procurementSettings,
|
||
|
|
inventory_settings: inventorySettings,
|
||
|
|
production_settings: productionSettings,
|
||
|
|
supplier_settings: supplierSettings,
|
||
|
|
pos_settings: posSettings,
|
||
|
|
order_settings: orderSettings,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
setHasUnsavedChanges(false);
|
||
|
|
addToast('Ajustes guardados correctamente', { type: 'success' });
|
||
|
|
} catch (error) {
|
||
|
|
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
||
|
|
addToast(`Error al guardar ajustes: ${errorMessage}`, { type: 'error' });
|
||
|
|
} finally {
|
||
|
|
setIsSaving(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleResetAll = () => {
|
||
|
|
if (settings) {
|
||
|
|
setProcurementSettings(settings.procurement_settings);
|
||
|
|
setInventorySettings(settings.inventory_settings);
|
||
|
|
setProductionSettings(settings.production_settings);
|
||
|
|
setSupplierSettings(settings.supplier_settings);
|
||
|
|
setPosSettings(settings.pos_settings);
|
||
|
|
setOrderSettings(settings.order_settings);
|
||
|
|
setHasUnsavedChanges(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleCategoryChange = (category: string) => {
|
||
|
|
setHasUnsavedChanges(true);
|
||
|
|
};
|
||
|
|
|
||
|
|
if (isLoading || !currentTenant) {
|
||
|
|
return (
|
||
|
|
<div className="p-6 space-y-6">
|
||
|
|
<PageHeader
|
||
|
|
title="Ajustes"
|
||
|
|
description="Configura los parámetros operativos de tu panadería"
|
||
|
|
/>
|
||
|
|
<div className="flex items-center justify-center h-64">
|
||
|
|
<Loader className="w-8 h-8 animate-spin text-[var(--color-primary)]" />
|
||
|
|
<span className="ml-2 text-[var(--text-secondary)]">Cargando ajustes...</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
return (
|
||
|
|
<div className="p-6 space-y-6">
|
||
|
|
<PageHeader
|
||
|
|
title="Ajustes"
|
||
|
|
description="Error al cargar los ajustes"
|
||
|
|
/>
|
||
|
|
<Card className="p-6">
|
||
|
|
<div className="text-red-600">
|
||
|
|
Error al cargar los ajustes: {error.message || 'Error desconocido'}
|
||
|
|
</div>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="p-6 space-y-6 pb-32">
|
||
|
|
<PageHeader
|
||
|
|
title="Ajustes"
|
||
|
|
description="Configura los parámetros operativos de tu panadería"
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Top Action Bar */}
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<div className="flex items-center gap-2 text-sm">
|
||
|
|
<Settings className="w-4 h-4 text-[var(--color-primary)]" />
|
||
|
|
<span className="text-[var(--text-secondary)]">
|
||
|
|
Ajusta los parámetros según las necesidades de tu negocio
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
onClick={handleResetAll}
|
||
|
|
disabled={!hasUnsavedChanges || isSaving}
|
||
|
|
>
|
||
|
|
<RotateCcw className="w-4 h-4" />
|
||
|
|
Restablecer Todo
|
||
|
|
</Button>
|
||
|
|
<Button
|
||
|
|
variant="primary"
|
||
|
|
size="sm"
|
||
|
|
onClick={handleSaveAll}
|
||
|
|
isLoading={isSaving}
|
||
|
|
disabled={!hasUnsavedChanges}
|
||
|
|
loadingText="Guardando..."
|
||
|
|
>
|
||
|
|
<Save className="w-4 h-4" />
|
||
|
|
Guardar Cambios
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Settings Categories */}
|
||
|
|
<div className="space-y-6">
|
||
|
|
{/* Procurement Settings */}
|
||
|
|
{procurementSettings && (
|
||
|
|
<ProcurementSettingsCard
|
||
|
|
settings={procurementSettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setProcurementSettings(newSettings);
|
||
|
|
handleCategoryChange('procurement');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Inventory Settings */}
|
||
|
|
{inventorySettings && (
|
||
|
|
<InventorySettingsCard
|
||
|
|
settings={inventorySettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setInventorySettings(newSettings);
|
||
|
|
handleCategoryChange('inventory');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Production Settings */}
|
||
|
|
{productionSettings && (
|
||
|
|
<ProductionSettingsCard
|
||
|
|
settings={productionSettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setProductionSettings(newSettings);
|
||
|
|
handleCategoryChange('production');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Supplier Settings */}
|
||
|
|
{supplierSettings && (
|
||
|
|
<SupplierSettingsCard
|
||
|
|
settings={supplierSettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setSupplierSettings(newSettings);
|
||
|
|
handleCategoryChange('supplier');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* POS Settings */}
|
||
|
|
{posSettings && (
|
||
|
|
<POSSettingsCard
|
||
|
|
settings={posSettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setPosSettings(newSettings);
|
||
|
|
handleCategoryChange('pos');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Order Settings */}
|
||
|
|
{orderSettings && (
|
||
|
|
<OrderSettingsCard
|
||
|
|
settings={orderSettings}
|
||
|
|
onChange={(newSettings) => {
|
||
|
|
setOrderSettings(newSettings);
|
||
|
|
handleCategoryChange('order');
|
||
|
|
}}
|
||
|
|
disabled={isSaving}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Floating Save Banner */}
|
||
|
|
{hasUnsavedChanges && (
|
||
|
|
<div className="fixed bottom-6 right-6 z-50">
|
||
|
|
<Card className="p-4 shadow-lg border-2 border-[var(--color-primary)]">
|
||
|
|
<div className="flex items-center gap-3">
|
||
|
|
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
||
|
|
<AlertCircle className="w-4 h-4 text-yellow-500" />
|
||
|
|
Tienes cambios sin guardar
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
onClick={handleResetAll}
|
||
|
|
disabled={isSaving}
|
||
|
|
>
|
||
|
|
<RotateCcw className="w-4 h-4" />
|
||
|
|
Descartar
|
||
|
|
</Button>
|
||
|
|
<Button
|
||
|
|
variant="primary"
|
||
|
|
size="sm"
|
||
|
|
onClick={handleSaveAll}
|
||
|
|
isLoading={isSaving}
|
||
|
|
loadingText="Guardando..."
|
||
|
|
>
|
||
|
|
<Save className="w-4 h-4" />
|
||
|
|
Guardar
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default AjustesPage;
|