feat: простенький экран отчетов
This commit is contained in:
@@ -10,7 +10,7 @@ internal class BaseStorageContractTest
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetUp()
|
||||
{
|
||||
BankDbContext = new BankDbContext(new ConfigurationDatabase());
|
||||
BankDbContext = new BankDbContext(new Infrastructure.ConfigurationDatabase());
|
||||
|
||||
BankDbContext.Database.EnsureDeleted();
|
||||
BankDbContext.Database.EnsureCreated();
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ClientAdapter : IClientAdapter
|
||||
|
||||
// Mapping for Deposit
|
||||
cfg.CreateMap<DepositDataModel, DepositViewModel>()
|
||||
.ForMember(dest => dest.DepositClients, opt => opt.MapFrom(src => src.Currencies)); // Adjust if Currencies is meant to map to DepositClients
|
||||
.ForMember(dest => dest.DepositCurrencies, opt => opt.MapFrom(src => src.Currencies)); // Adjust if Currencies is meant to map to DepositClients
|
||||
|
||||
// Mapping for ClientCreditProgram
|
||||
cfg.CreateMap<ClientCreditProgramBindingModel, ClientCreditProgramDataModel>();
|
||||
|
||||
@@ -8,16 +8,10 @@ namespace BankWebApi.Controllers;
|
||||
[Authorize]
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class ReportController : ControllerBase
|
||||
public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
{
|
||||
private readonly IReportAdapter _adapter;
|
||||
private readonly EmailService _emailService;
|
||||
|
||||
public ReportController(IReportAdapter adapter)
|
||||
{
|
||||
_adapter = adapter;
|
||||
_emailService = EmailService.CreateYandexService();
|
||||
}
|
||||
private readonly IReportAdapter _adapter = adapter;
|
||||
private readonly EmailService _emailService = EmailService.CreateYandexService();
|
||||
|
||||
[HttpGet]
|
||||
[Consumes("application/json")]
|
||||
|
||||
Binary file not shown.
@@ -32,6 +32,7 @@
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.1.0",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-doc-viewer": "^0.1.14",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.56.4",
|
||||
"react-router-dom": "^7.6.0",
|
||||
|
||||
BIN
TheBank/bankui/public/Shrek.png
Normal file
BIN
TheBank/bankui/public/Shrek.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
@@ -1,9 +1,10 @@
|
||||
import { useAuthCheck } from '@/hooks/useAuthCheck';
|
||||
import { useAuthStore } from '@/store/workerStore';
|
||||
import { Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import { Link, Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { Footer } from '@/components/layout/Footer';
|
||||
import { Suspense } from 'react';
|
||||
import { Button } from './components/ui/button';
|
||||
|
||||
function App() {
|
||||
const user = useAuthStore((store) => store.user);
|
||||
@@ -21,10 +22,27 @@ function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
{location.pathname === '/' && (
|
||||
<main className="flex justify-center items-center">
|
||||
<div className="flex-1 flex justify-center items-center">
|
||||
<img className="block" src="/Shrek.png" alt="кладовщик" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div>Удобный сервис для кладовщиков</div>
|
||||
<Link to="/storekeepers">
|
||||
<Button>За работу</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
)}
|
||||
{location.pathname !== '/' && (
|
||||
<>
|
||||
<Header />
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
</>
|
||||
)}
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
getData,
|
||||
getReport,
|
||||
getSingleData,
|
||||
postData,
|
||||
postLoginData,
|
||||
@@ -129,3 +130,8 @@ export const storekeepersApi = {
|
||||
getCurrentUser: () =>
|
||||
getSingleData<StorekeeperBindingModel>('api/storekeepers/me'),
|
||||
};
|
||||
|
||||
//Reports API
|
||||
export const reportsApi = {
|
||||
loadClientsByCreditProgram: () => getReport('path'),
|
||||
};
|
||||
|
||||
@@ -69,3 +69,135 @@ export async function putData<T>(path: string, data: T) {
|
||||
throw new Error(`Не получается загрузить ${path}: ${res.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
// report api
|
||||
export interface ReportParams {
|
||||
fromDate?: string; // Например, '2025-01-01'
|
||||
toDate?: string; // Например, '2025-05-21'
|
||||
}
|
||||
|
||||
export type ReportType =
|
||||
| 'clientsByCreditProgram'
|
||||
| 'clientsByDeposit'
|
||||
| 'depositByCreditProgram'
|
||||
| 'depositAndCreditProgramByCurrency';
|
||||
export type ReportFormat = 'word' | 'excel' | 'pdf';
|
||||
|
||||
export async function getReport(
|
||||
reportType: ReportType,
|
||||
format: ReportFormat,
|
||||
params?: ReportParams,
|
||||
): Promise<{ blob: Blob; fileName: string; mimeType: string }> {
|
||||
const actionMap: Record<ReportType, Record<ReportFormat, string>> = {
|
||||
clientsByCreditProgram: {
|
||||
word: 'LoadClientsByCreditProgram',
|
||||
excel: 'LoadExcelClientByCreditProgram',
|
||||
pdf: 'LoadPdfClientsByCreditProgram',
|
||||
},
|
||||
clientsByDeposit: {
|
||||
word: 'LoadClientsByDeposit',
|
||||
excel: 'LoadExcelClientsByDeposit',
|
||||
pdf: 'LoadPdfClientsByDeposit',
|
||||
},
|
||||
depositByCreditProgram: {
|
||||
word: 'LoadDepositByCreditProgram',
|
||||
excel: 'LoadExcelDepositByCreditProgram',
|
||||
pdf: 'LoadPdfDepositByCreditProgram',
|
||||
},
|
||||
depositAndCreditProgramByCurrency: {
|
||||
word: 'LoadDepositAndCreditProgramByCurrency',
|
||||
excel: 'LoadExcelDepositAndCreditProgramByCurrency',
|
||||
pdf: 'LoadPdfDepositAndCreditProgramByCurrency',
|
||||
},
|
||||
};
|
||||
|
||||
const action = actionMap[reportType][format];
|
||||
let query = '';
|
||||
if (params) {
|
||||
const paramParts: string[] = [];
|
||||
if (params.fromDate)
|
||||
paramParts.push(`fromDate=${encodeURIComponent(params.fromDate)}`);
|
||||
if (params.toDate)
|
||||
paramParts.push(`toDate=${encodeURIComponent(params.toDate)}`);
|
||||
if (paramParts.length > 0) query = `?${paramParts.join('&')}`;
|
||||
}
|
||||
|
||||
const url = `${API_URL}/api/Reports/${action}${query}`;
|
||||
const res = await fetch(url, {
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Не удалось загрузить отчет ${reportType} (${format}): ${res.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const blob = await res.blob();
|
||||
const contentDisposition = res.headers.get('Content-Disposition');
|
||||
let fileName = `${reportType}.${format}`;
|
||||
|
||||
if (contentDisposition && contentDisposition.includes('filename=')) {
|
||||
fileName = contentDisposition
|
||||
.split('filename=')[1]
|
||||
.replace(/"/g, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
const mimeType =
|
||||
res.headers.get('Content-Type') ||
|
||||
{
|
||||
word: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
excel:
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
pdf: 'application/pdf',
|
||||
}[format];
|
||||
|
||||
return { blob, fileName, mimeType };
|
||||
}
|
||||
|
||||
export async function sendReportByEmail(
|
||||
reportType: ReportType,
|
||||
format: ReportFormat,
|
||||
email: string,
|
||||
params?: ReportParams,
|
||||
): Promise<void> {
|
||||
const actionMap: Record<ReportType, Record<ReportFormat, string>> = {
|
||||
clientsByCreditProgram: {
|
||||
word: 'SendReportByCreditProgram',
|
||||
excel: 'SendExcelReportByCreditProgram',
|
||||
pdf: 'SendPdfReportByCreditProgram',
|
||||
},
|
||||
clientsByDeposit: {
|
||||
word: 'SendReportByDeposit',
|
||||
excel: 'SendExcelReportByDeposit',
|
||||
pdf: 'SendPdfReportByDeposit',
|
||||
},
|
||||
depositByCreditProgram: {
|
||||
word: 'SendReportDepositByCreditProgram',
|
||||
excel: 'SendExcelReportDepositByCreditProgram',
|
||||
pdf: 'SendPdfReportDepositByCreditProgram',
|
||||
},
|
||||
depositAndCreditProgramByCurrency: {
|
||||
word: 'SendReportByCurrency',
|
||||
excel: 'SendExcelReportByCurrency',
|
||||
pdf: 'SendPdfReportByCurrency',
|
||||
},
|
||||
};
|
||||
|
||||
const action = actionMap[reportType][format];
|
||||
const res = await fetch(`${API_URL}/api/Reports/${action}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ email, ...params }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Не удалось отправить отчет ${reportType} (${format}): ${res.statusText}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
45
TheBank/bankui/src/components/features/ExcelViewer.tsx
Normal file
45
TheBank/bankui/src/components/features/ExcelViewer.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import DocViewer, { DocViewerRenderers } from 'react-doc-viewer';
|
||||
import { useReports } from '@/hooks/useReports';
|
||||
import { type ReportType, type ReportParams } from '@/api/client';
|
||||
|
||||
interface ExcelViewerProps {
|
||||
reportType: ReportType;
|
||||
params?: ReportParams;
|
||||
}
|
||||
|
||||
export const ExcelViewer = ({ reportType, params }: ExcelViewerProps) => {
|
||||
const { excelReport, isExcelLoading, isExcelError, excelError } = useReports(
|
||||
reportType,
|
||||
params,
|
||||
);
|
||||
const [documents, setDocuments] = React.useState<
|
||||
{ uri: string; fileType: string }[]
|
||||
>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (excelReport?.blob) {
|
||||
const uri = URL.createObjectURL(excelReport.blob);
|
||||
setDocuments([{ uri, fileType: 'xlsx' }]);
|
||||
return () => URL.revokeObjectURL(uri);
|
||||
}
|
||||
}, [excelReport]);
|
||||
|
||||
if (isExcelLoading) return <div className="p-4">Загрузка Excel...</div>;
|
||||
if (isExcelError)
|
||||
return (
|
||||
<div className="p-4 text-red-500">Ошибка: {excelError?.message}</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{documents.length > 0 && (
|
||||
<DocViewer
|
||||
documents={documents}
|
||||
pluginRenderers={DocViewerRenderers}
|
||||
style={{ height: '500px' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
43
TheBank/bankui/src/components/features/PdfViewer.tsx
Normal file
43
TheBank/bankui/src/components/features/PdfViewer.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import DocViewer, { DocViewerRenderers } from 'react-doc-viewer';
|
||||
import { useReports } from '@/hooks/useReports';
|
||||
import { type ReportType, type ReportParams } from '@/api/client';
|
||||
|
||||
interface PdfViewerProps {
|
||||
reportType: ReportType;
|
||||
params?: ReportParams;
|
||||
}
|
||||
|
||||
export const PdfViewer = ({ reportType, params }: PdfViewerProps) => {
|
||||
const { pdfReport, isPdfLoading, isPdfError, pdfError } = useReports(
|
||||
reportType,
|
||||
params,
|
||||
);
|
||||
const [documents, setDocuments] = React.useState<
|
||||
{ uri: string; fileType: string }[]
|
||||
>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (pdfReport?.blob) {
|
||||
const uri = URL.createObjectURL(pdfReport.blob);
|
||||
setDocuments([{ uri, fileType: 'pdf' }]);
|
||||
return () => URL.revokeObjectURL(uri);
|
||||
}
|
||||
}, [pdfReport]);
|
||||
|
||||
if (isPdfLoading) return <div className="p-4">Загрузка PDF...</div>;
|
||||
if (isPdfError)
|
||||
return <div className="p-4 text-red-500">Ошибка: {pdfError?.message}</div>;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{documents.length > 0 && (
|
||||
<DocViewer
|
||||
documents={documents}
|
||||
pluginRenderers={DocViewerRenderers}
|
||||
style={{ height: '500px' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
36
TheBank/bankui/src/components/features/ReportViewer.tsx
Normal file
36
TheBank/bankui/src/components/features/ReportViewer.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import type { SelectedReport } from '../pages/Reports';
|
||||
import { Button } from '../ui/button';
|
||||
import { PdfViewer } from './PdfViewer';
|
||||
import { WordViewer } from './WordViewer';
|
||||
import { ExcelViewer } from './ExcelViewer';
|
||||
|
||||
type ReportViewerProps = {
|
||||
selectedReport: SelectedReport;
|
||||
};
|
||||
|
||||
export const ReportViewer = ({
|
||||
selectedReport,
|
||||
}: ReportViewerProps): React.JSX.Element => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex gap-10">
|
||||
<Button>Сгенерировать</Button>
|
||||
<Button>Сохранить</Button>
|
||||
<Button>Отправить</Button>
|
||||
</div>
|
||||
<div>
|
||||
{selectedReport === 'pdf' && (
|
||||
<PdfViewer reportType={'clientsByCreditProgram'} />
|
||||
)}
|
||||
{selectedReport === 'word' && (
|
||||
<WordViewer reportType={'clientsByCreditProgram'} />
|
||||
)}
|
||||
{selectedReport === 'excel' && (
|
||||
<ExcelViewer reportType={'clientsByCreditProgram'} />
|
||||
)}
|
||||
{!selectedReport && <>не выбран отчет</>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
43
TheBank/bankui/src/components/features/WordViewer.tsx
Normal file
43
TheBank/bankui/src/components/features/WordViewer.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import DocViewer, { DocViewerRenderers } from 'react-doc-viewer';
|
||||
import { useReports } from '@/hooks/useReports';
|
||||
import { type ReportType, type ReportParams } from '@/api/client';
|
||||
|
||||
interface WordViewerProps {
|
||||
reportType: ReportType;
|
||||
params?: ReportParams;
|
||||
}
|
||||
|
||||
export const WordViewer = ({ reportType, params }: WordViewerProps) => {
|
||||
const { wordReport, isWordLoading, isWordError, wordError } = useReports(
|
||||
reportType,
|
||||
params,
|
||||
);
|
||||
const [documents, setDocuments] = React.useState<
|
||||
{ uri: string; fileType: string }[]
|
||||
>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (wordReport?.blob) {
|
||||
const uri = URL.createObjectURL(wordReport.blob);
|
||||
setDocuments([{ uri, fileType: 'docx' }]);
|
||||
return () => URL.revokeObjectURL(uri);
|
||||
}
|
||||
}, [wordReport]);
|
||||
|
||||
if (isWordLoading) return <div className="p-4">Загрузка Word...</div>;
|
||||
if (isWordError)
|
||||
return <div className="p-4 text-red-500">Ошибка: {wordError?.message}</div>;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{documents.length > 0 && (
|
||||
<DocViewer
|
||||
documents={documents}
|
||||
pluginRenderers={DocViewerRenderers}
|
||||
style={{ height: '500px' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
import { Avatar, AvatarFallback } from '../ui/avatar';
|
||||
import { Button } from '../ui/button';
|
||||
import { useAuthStore } from '@/store/workerStore';
|
||||
import { useStorekeepers } from '@/hooks/useStorekeepers';
|
||||
|
||||
type NavOptionValue = {
|
||||
name: string;
|
||||
@@ -72,6 +71,16 @@ const navOptions = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Отчеты',
|
||||
options: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Выгрузить отчеты',
|
||||
link: '/reports',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const Header = (): React.JSX.Element => {
|
||||
|
||||
49
TheBank/bankui/src/components/layout/ReportSidebar.tsx
Normal file
49
TheBank/bankui/src/components/layout/ReportSidebar.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarGroupContent,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarProvider,
|
||||
SidebarTrigger,
|
||||
} from '@/components/ui/sidebar';
|
||||
|
||||
type SidebarProps = {
|
||||
onWordClick: () => void;
|
||||
onPdfClick: () => void;
|
||||
onExcelClick: () => void;
|
||||
};
|
||||
export const ReportSidebar = ({
|
||||
onWordClick,
|
||||
onExcelClick,
|
||||
onPdfClick,
|
||||
}: SidebarProps): React.JSX.Element => {
|
||||
return (
|
||||
<SidebarProvider className="w-[400px]">
|
||||
<Sidebar variant="floating" collapsible="none">
|
||||
<SidebarContent />
|
||||
<SidebarGroupContent className="">
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild onClick={onWordClick}>
|
||||
<span>отчет word КЛАДОВЩИКА</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild onClick={onExcelClick}>
|
||||
<span>отчет excel КЛАДОВЩИКА</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild onClick={onPdfClick}>
|
||||
<span>отчет pdf КЛАДОВЩИКА</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</Sidebar>
|
||||
</SidebarProvider>
|
||||
);
|
||||
};
|
||||
20
TheBank/bankui/src/components/pages/Reports.tsx
Normal file
20
TheBank/bankui/src/components/pages/Reports.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { ReportSidebar } from '../layout/ReportSidebar';
|
||||
import { ReportViewer } from '../features/ReportViewer';
|
||||
|
||||
export type SelectedReport = 'word' | 'pdf' | 'excel' | undefined;
|
||||
|
||||
export const Reports = (): React.JSX.Element => {
|
||||
const [selectedReport, setSelectedReport] = React.useState<SelectedReport>();
|
||||
|
||||
return (
|
||||
<main className="flex">
|
||||
<ReportSidebar
|
||||
onWordClick={() => setSelectedReport('word')}
|
||||
onPdfClick={() => setSelectedReport('pdf')}
|
||||
onExcelClick={() => setSelectedReport('excel')}
|
||||
/>
|
||||
<ReportViewer selectedReport={selectedReport} />
|
||||
</main>
|
||||
);
|
||||
};
|
||||
66
TheBank/bankui/src/hooks/useReports.tsx
Normal file
66
TheBank/bankui/src/hooks/useReports.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
// reportsApi.ts
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import {
|
||||
getReport,
|
||||
sendReportByEmail,
|
||||
type ReportParams,
|
||||
type ReportType,
|
||||
type ReportFormat,
|
||||
} from '@/api/client';
|
||||
|
||||
export const useReports = (reportType: ReportType, params?: ReportParams) => {
|
||||
const requiresDates =
|
||||
reportType === 'clientsByDeposit' ||
|
||||
reportType === 'depositAndCreditProgramByCurrency';
|
||||
const isEnabled: boolean =
|
||||
Boolean(reportType) &&
|
||||
(!requiresDates || (Boolean(params?.fromDate) && Boolean(params?.toDate)));
|
||||
|
||||
const pdfQuery = useQuery({
|
||||
queryKey: ['pdf-document', reportType, params] as const,
|
||||
queryFn: () => getReport(reportType, 'pdf', params),
|
||||
enabled: isEnabled,
|
||||
});
|
||||
|
||||
const wordQuery = useQuery({
|
||||
queryKey: ['word-document', reportType, params] as const,
|
||||
queryFn: () => getReport(reportType, 'word', params),
|
||||
enabled: isEnabled,
|
||||
});
|
||||
|
||||
const excelQuery = useQuery({
|
||||
queryKey: ['excel-document', reportType, params] as const,
|
||||
queryFn: () => getReport(reportType, 'excel', params),
|
||||
enabled: isEnabled,
|
||||
});
|
||||
|
||||
const sendReport = useMutation({
|
||||
mutationFn: ({
|
||||
reportType,
|
||||
format,
|
||||
email,
|
||||
params,
|
||||
}: {
|
||||
reportType: ReportType;
|
||||
format: ReportFormat;
|
||||
email: string;
|
||||
params?: ReportParams;
|
||||
}) => sendReportByEmail(reportType, format, email, params),
|
||||
});
|
||||
|
||||
return {
|
||||
pdfReport: pdfQuery.data,
|
||||
pdfError: pdfQuery.error,
|
||||
isPdfError: pdfQuery.isError,
|
||||
isPdfLoading: pdfQuery.isLoading,
|
||||
wordReport: wordQuery.data,
|
||||
wordError: wordQuery.error,
|
||||
isWordError: wordQuery.isError,
|
||||
isWordLoading: wordQuery.isLoading,
|
||||
excelReport: excelQuery.data,
|
||||
excelError: excelQuery.error,
|
||||
isExcelError: excelQuery.isError,
|
||||
isExcelLoading: excelQuery.isLoading,
|
||||
sendReport,
|
||||
};
|
||||
};
|
||||
@@ -15,6 +15,7 @@ import { Storekeepers } from './components/pages/Storekeepers.tsx';
|
||||
import { Periods } from './components/pages/Periods.tsx';
|
||||
import { Toaster } from './components/ui/sonner.tsx';
|
||||
import { Profile } from './components/pages/Profile.tsx';
|
||||
import { Reports } from './components/pages/Reports.tsx';
|
||||
|
||||
const routes = createBrowserRouter([
|
||||
{
|
||||
@@ -41,6 +42,10 @@ const routes = createBrowserRouter([
|
||||
path: '/profile',
|
||||
element: <Profile />,
|
||||
},
|
||||
{
|
||||
path: '/reports',
|
||||
element: <Reports />,
|
||||
},
|
||||
],
|
||||
errorElement: <p>бля пизда рулям</p>,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user