Improve the frontend and repository layer
This commit is contained in:
299
frontend/src/pages/app/database/ajustes/AjustesPage.tsx
Normal file
299
frontend/src/pages/app/database/ajustes/AjustesPage.tsx
Normal file
@@ -0,0 +1,299 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user