diff --git a/frontend_reactjs/src/hooks/swrHooks.ts b/frontend_reactjs/src/hooks/swrHooks.ts index f6bbe9e..da0d929 100644 --- a/frontend_reactjs/src/hooks/swrHooks.ts +++ b/frontend_reactjs/src/hooks/swrHooks.ts @@ -99,17 +99,33 @@ export function useFileType(fileName?: string | null, file?: Blob | null) { } } -export function useReport(city_id?: number) { +export function useReport(city_id?: number | null) { const { data, error, isLoading } = useSWR( city_id ? `/info/reports/${city_id}?to_export=false` : null, - fetcher, + (url) => fetcher(url, BASE_URL.info), + { + revalidateOnFocus: false + } + ) + + return { + report: data ? JSON.parse(data) : [], + isLoading, + isError: error + } +} + +export function useReportExport(city_id?: number | null, to_export?: boolean) { + const { data, error, isLoading } = useSWR( + city_id && to_export ? `/info/reports/${city_id}?to_export=${to_export}` : null, + (url) => fetcher(url, BASE_URL.info, 'blob'), { revalidateOnFocus: false } ) return { - report: JSON.parse(data), + reportExported: data ? data : null, isLoading, isError: error } @@ -150,7 +166,7 @@ export function useRegions(limit?: number, page?: number, search?: string | null } } -export function useCities(limit?: number, page?: number, search?: string) { +export function useCities(limit?: number, page?: number, search?: string | null) { const { data, error, isLoading } = useSWR( `/general/cities?limit=${limit || 10}&page=${page || 1}${search ? `&search=${search}` : ''}`, (url) => fetcher(url, BASE_URL.fuel), diff --git a/frontend_reactjs/src/pages/Reports.tsx b/frontend_reactjs/src/pages/Reports.tsx index 6ecf1dd..96b5b10 100644 --- a/frontend_reactjs/src/pages/Reports.tsx +++ b/frontend_reactjs/src/pages/Reports.tsx @@ -1,82 +1,134 @@ -import { useState } from "react" -import { Box, Button } from "@mui/material" +import { Fragment, useEffect, useState } from "react" +import { Autocomplete, Box, Button, CircularProgress, IconButton, TextField } from "@mui/material" import axiosInstance from "../http/axiosInstance" import { DataGrid } from "@mui/x-data-grid" import { BASE_URL } from "../constants" +import { useCities, useReport, useReportExport } from "../hooks/swrHooks" +import { useDebounce } from "@uidotdev/usehooks" +import { ICity } from "../interfaces/fuel" +import { Update } from "@mui/icons-material" +import { mutate } from "swr" export default function Reports() { - const [state, setState] = useState(null) + const [download, setDownload] = useState(false) - const fetch = async () => { - await axiosInstance.get(`/info/reports/0?to_export=true`, { - responseType: 'blob', - baseURL: BASE_URL.info - }).then(response => { - const url = window.URL.createObjectURL(response.data) + const [options, setOptions] = useState([]) + const [search, setSearch] = useState("") + const debouncedSearch = useDebounce(search, 500) + const [selectedOption, setSelectedOption] = useState(null) + const { cities, isLoading } = useCities(10, 1, debouncedSearch) + + const { report, isLoading: reportLoading } = useReport(selectedOption?.id) + + const { reportExported } = useReportExport(selectedOption?.id, download) + + useEffect(() => { + if (cities) { + setOptions([...cities]) + } + }, [cities]) + + const refreshReport = async () => { + mutate(`/info/reports/${selectedOption?.id}?to_export=false`) + } + + useEffect(() => { + if (selectedOption && reportExported && download) { + const url = window.URL.createObjectURL(reportExported) const link = document.createElement('a') link.href = url link.setAttribute('download', 'report.xlsx') document.body.appendChild(link); link.click(); link.remove(); - }) - } + setDownload(false) + } + }, [selectedOption, reportExported, download]) - const fetchBlob = async () => { - await axiosInstance.get(`/info/reports/0`).then(response => { - setState(JSON.parse(response.data)) - }) + const exportReport = async () => { + setDownload(true) } return ( <> - - + + setSearch(value)} + onChange={(_, value) => setSelectedOption(value)} + isOptionEqualToValue={(option: ICity, value: ICity) => option.id === value.id} + getOptionLabel={(option: ICity) => option.name ? option.name : ""} + options={options} + loading={isLoading} + value={selectedOption} + renderInput={(params) => ( + + {isLoading ? : null} + {params.InputProps.endAdornment} + + ) + }} + /> + )} + /> - - {state && - Object.keys(state[key])))].map(id => { - const row: any = { id: Number(id) }; - Object.keys(state).forEach(key => { - row[key] = state[key][id]; - }); - return row; - })} - columns={[ - { field: 'id', headerName: '№', width: 70 }, - ...Object.keys(state).map(key => ({ - field: key, - headerName: key.charAt(0).toUpperCase() + key.slice(1), - width: 150 - })) - ]} - initialState={{ - pagination: { - paginationModel: { page: 0, pageSize: 10 }, - }, - }} - pageSizeOptions={[10, 20, 50, 100]} - checkboxSelection={false} - disableRowSelectionOnClick + Object.keys(report[key])))].map(id => { + const row: any = { id: Number(id) }; + Object.keys(report).forEach(key => { + row[key] = report[key][id]; + }); + return row; + }) + : + [] + } + columns={[ + { field: 'id', headerName: '№', width: 70 }, + ...Object.keys(report).map(key => ({ + field: key, + headerName: key.charAt(0).toUpperCase() + key.slice(1), + width: 150 + })) + ]} + initialState={{ + pagination: { + paginationModel: { page: 0, pageSize: 10 }, + }, + }} + pageSizeOptions={[10, 20, 50, 100]} + checkboxSelection={false} + disableRowSelectionOnClick - processRowUpdate={(updatedRow) => { - return updatedRow - }} + processRowUpdate={(updatedRow) => { + return updatedRow + }} - onProcessRowUpdateError={() => { - }} - /> - } + onProcessRowUpdateError={() => { + }} + /> )