fuel; nest api

This commit is contained in:
2025-12-23 09:53:04 +09:00
parent fa516b3a20
commit 04ce74d320
23 changed files with 1125 additions and 183 deletions

View File

@ -0,0 +1,287 @@
import { Button, Dialog, DialogBody, DialogContent, DialogSurface, DialogTitle, Field, Input, ProgressBar, Spinner } from '@fluentui/react-components'
import { DataPieColor } from '@fluentui/react-icons'
import axios from 'axios'
import { useEffect, useState } from 'react'
import { Controller, SubmitHandler, useFieldArray, useForm, useWatch } from 'react-hook-form'
import useSWR from 'swr'
type Month = {
month: number
value: number
}
type Inputs = {
id_boiler: string
id_fuel: string
year: number
months: Month[]
}
const months = [
{
id: 7,
month: 'jul',
label: 'Июль'
},
{
id: 8,
month: 'aug',
label: 'Август'
},
{
id: 9,
month: 'sep',
label: 'Сентябрь'
},
{
id: 10,
month: 'nov',
label: 'Октябрь'
},
{
id: 11,
month: 'oct',
label: 'Ноябрь'
},
{
id: 12,
month: 'dec',
label: 'Декабрь'
},
{
id: 1,
month: 'jan',
label: 'Январь'
},
{
id: 2,
month: 'feb',
label: 'Февраль'
},
{
id: 3,
month: 'mar',
label: 'Март'
},
{
id: 4,
month: 'apr',
label: 'Апрель'
},
{
id: 5,
month: 'may',
label: 'Май'
},
{
id: 6,
month: 'jun',
label: 'Июнь'
},
]
const LimitAddForm = ({
cityId,
open,
setOpen,
percentage
}: {
cityId: number | undefined
open: boolean
setOpen: (open: boolean) => void
percentage?: boolean
}) => {
const {
handleSubmit,
control,
setValue,
formState: { isSubmitting },
} = useForm<Inputs>({
defaultValues: {
months: Array.from({ length: 12 }, (_, i) => ({
month: i + 1,
value: 0,
}))
}
})
const { fields } = useFieldArray({
control,
name: 'months'
})
const onSubmit: SubmitHandler<Inputs> = async (data) => {
await axios.post(`/fuel/limits`, {
id_boiler: '06407B1C-C23F-44C8-BADF-4653060EB784',
id_fuel: 3,
year: 2025,
months: data.months
}, {
baseURL: import.meta.env.VITE_API_NEST_URL,
})
//mutateFuels([...fuels, data])
// setTimeout(() => {
// console.log("done")
// }, 1000)
await new Promise((resolve) => {
setTimeout(() => resolve(console.log("done")), 1000);
})
}
const [overallLimit, setOverallLimit] = useState(0)
const watchedMonths = useWatch({
control,
name: "months",
})
const { data: citySettings } = useSWR(
cityId ? `/fuel/city-settings?city_id=${cityId}` : null,
() =>
axios
.get(`/fuel/city-settings?city_id=${cityId}`, {
baseURL: import.meta.env.VITE_API_NEST_URL,
})
.then((res) => res.data)
)
const handlePartition = () => {
if (citySettings && Array.isArray(citySettings) && citySettings.length > 0) {
citySettings.map(s => {
setValue(`months.${Number(s.month - 1)}.value`, Number((Number((overallLimit / 100).toFixed(3)) * s.procent).toFixed(3)))
})
}
}
useEffect(() => {
if (watchedMonths) {
let sum = 0
watchedMonths.map(wm => sum = sum + wm.value)
setOverallLimit(Number(sum.toFixed(3)))
}
}, [watchedMonths])
return (
<Dialog open={open} onOpenChange={(_, data) => setOpen(data.open)}>
<DialogSurface>
<DialogBody>
{isSubmitting &&
<div style={{ position: 'absolute', inset: 0, zIndex: '1', background: '#00000030', display: 'flex', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100%' }}>
<Spinner />
</div>
}
<DialogTitle>Распределение лимитов</DialogTitle>
<DialogContent
style={{
display: 'flex', flexDirection: 'column', gap: '1rem',
minWidth: 'fit-content', minHeight: 'fit-content'
}}
>
<form onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
handlePartition()
}} style={{ display: 'flex', width: '100%', gap: '0.25rem', justifyContent: 'flex-end' }}>
<Field style={{ display: 'flex' }} label='Лимит расхода котельного топлива за сезон' orientation='horizontal'>
<Input type='number' value={overallLimit.toString()} onChange={(_, data) => setOverallLimit(Number(data.value))} />
</Field>
<Button title='Распределить' type='submit' icon={<DataPieColor />}></Button>
</form>
<form onSubmit={handleSubmit(onSubmit)}>
<div style={{ display: 'flex', flexDirection: 'row', gap: '1rem', width: '100%' }}>
<div style={{ display: 'flex', flexDirection: 'column', width: '50%', gap: '0.25rem' }}>
{fields.map((f, index) => {
if (f.month >= 7 && f.month <= 12) {
return (
<Controller
key={f.month}
name={`months.${index}.value`}
control={control}
render={({ field }) => (
<Field style={{ gridTemplateColumns: '1fr auto' }} key={f.month} validationMessage={percentage ? `${(field.value / (overallLimit / 100)).toFixed(1)}%` : undefined} validationState='none' label={months.find(m => m.id === f.month)?.label} orientation='horizontal'>
<Input value={field.value.toString()} onChange={(_, data) => field.onChange(Number(data.value))} />
{percentage && <ProgressBar value={field.value / (overallLimit / 100) * 0.01} />}
</Field>
)}
/>
)
}
})}
<Field style={{ gridTemplateColumns: '1fr auto' }} validationMessage={percentage ? `${(watchedMonths.filter(month => month.month >= 7 && month.month <= 12)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0) / (overallLimit / 100)).toFixed(1)}%` : undefined} validationState='none' label={'2 полугодие'} orientation='horizontal'>
<Input disabled value={watchedMonths.filter(month => month.month >= 7 && month.month <= 12)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0).toFixed(3)} />
{percentage && <ProgressBar value={watchedMonths.filter(month => month.month >= 7 && month.month <= 12)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0) / (overallLimit / 100) * 0.01} />}
</Field>
</div>
<div style={{ display: 'flex', flexDirection: 'column', width: '50%', gap: '0.25rem' }}>
{fields.map((f, index) => {
if (f.month >= 1 && f.month <= 6) {
return (
<Controller
key={f.month}
name={`months.${index}.value`}
control={control}
render={({ field }) => (
<Field style={{ gridTemplateColumns: '1fr auto' }} key={f.month} validationMessage={percentage ? `${(field.value / (overallLimit / 100)).toFixed(1)}%` : undefined} validationState='none' label={months.find(m => m.id === f.month)?.label} orientation='horizontal'>
<Input value={field.value.toString()} onChange={(_, data) => field.onChange(Number(data.value))} />
{percentage && <ProgressBar value={field.value / (overallLimit / 100) * 0.01} />}
</Field>
)}
/>
)
}
})}
<Field style={{ gridTemplateColumns: '1fr auto' }} validationMessage={percentage ? `${(watchedMonths.filter(month => month.month >= 1 && month.month <= 6)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0) / (overallLimit / 100)).toFixed(1)}%` : undefined} validationState='none' label={'1 полугодие'} orientation='horizontal'>
<Input disabled value={watchedMonths.filter(month => month.month >= 1 && month.month <= 6)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0).toFixed(3)} />
{percentage && <ProgressBar value={watchedMonths.filter(month => month.month >= 1 && month.month <= 6)
.reduce((sum, month) => {
const value = Number(month.value) || 0;
return sum + value;
}, 0) / (overallLimit / 100) * 0.01} />}
</Field>
</div>
</div>
<div style={{ display: 'flex', marginTop: '1rem', justifyContent: 'flex-end' }}>
<Button type='submit' appearance='primary'>
Добавить
</Button>
</div>
</form>
</DialogContent>
</DialogBody>
</DialogSurface>
</Dialog>
)
}
export default LimitAddForm