This commit is contained in:
110
frontend/src/components/SettingsPage/SettingsPage.tsx
Normal file
110
frontend/src/components/SettingsPage/SettingsPage.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
TextField,
|
||||
Button,
|
||||
Paper,
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { useSettingsQuery, useUpdateSettings } from '../../hooks/useSettings';
|
||||
import { DEFAULT_ESTIMATE_CONFIG } from '../../utils/estimate';
|
||||
|
||||
export function SettingsPage() {
|
||||
const { data: settings, isLoading } = useSettingsQuery();
|
||||
const updateSettings = useUpdateSettings();
|
||||
|
||||
const [hoursPerDay, setHoursPerDay] = useState(
|
||||
String(DEFAULT_ESTIMATE_CONFIG.hoursPerDay),
|
||||
);
|
||||
const [daysPerWeek, setDaysPerWeek] = useState(
|
||||
String(DEFAULT_ESTIMATE_CONFIG.daysPerWeek),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (settings) {
|
||||
setHoursPerDay(
|
||||
String(settings.hoursPerDay ?? DEFAULT_ESTIMATE_CONFIG.hoursPerDay),
|
||||
);
|
||||
setDaysPerWeek(
|
||||
String(settings.daysPerWeek ?? DEFAULT_ESTIMATE_CONFIG.daysPerWeek),
|
||||
);
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
const handleSave = () => {
|
||||
const hpd = Number(hoursPerDay);
|
||||
const dpw = Number(daysPerWeek);
|
||||
if (hpd > 0 && hpd <= 24 && dpw > 0 && dpw <= 7) {
|
||||
updateSettings.mutate({ hoursPerDay: hpd, daysPerWeek: dpw });
|
||||
}
|
||||
};
|
||||
|
||||
const hpdNum = Number(hoursPerDay);
|
||||
const dpwNum = Number(daysPerWeek);
|
||||
const isValid =
|
||||
hpdNum > 0 && hpdNum <= 24 && dpwNum > 0 && dpwNum <= 7;
|
||||
|
||||
if (isLoading) return null;
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: 500 }}>
|
||||
<Typography variant="h5" sx={{ mb: 3 }}>
|
||||
Настройки
|
||||
</Typography>
|
||||
|
||||
<Paper sx={{ p: 3 }}>
|
||||
<Typography variant="h6" sx={{ mb: 2 }}>
|
||||
Формат оценки трудозатрат
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Эти значения используются для конвертации оценок из формата «1w 3d 7h»
|
||||
в часы и обратно.
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<TextField
|
||||
label="Часов в рабочем дне"
|
||||
type="number"
|
||||
value={hoursPerDay}
|
||||
onChange={(e) => setHoursPerDay(e.target.value)}
|
||||
slotProps={{ htmlInput: { min: 1, max: 24 } }}
|
||||
helperText="От 1 до 24"
|
||||
error={!hpdNum || hpdNum < 1 || hpdNum > 24}
|
||||
size="small"
|
||||
/>
|
||||
<TextField
|
||||
label="Рабочих дней в неделе"
|
||||
type="number"
|
||||
value={daysPerWeek}
|
||||
onChange={(e) => setDaysPerWeek(e.target.value)}
|
||||
slotProps={{ htmlInput: { min: 1, max: 7 } }}
|
||||
helperText="От 1 до 7"
|
||||
error={!dpwNum || dpwNum < 1 || dpwNum > 7}
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mt: 3, display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
disabled={!isValid || updateSettings.isPending}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
{updateSettings.isSuccess && (
|
||||
<Alert severity="success" sx={{ py: 0 }}>
|
||||
Сохранено
|
||||
</Alert>
|
||||
)}
|
||||
{updateSettings.isError && (
|
||||
<Alert severity="error" sx={{ py: 0 }}>
|
||||
Ошибка сохранения
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user