This commit is contained in:
cracklesparkle
2024-07-10 14:37:00 +09:00
parent a65a431b09
commit f9de1124c3
36 changed files with 312 additions and 176 deletions

View File

@ -29,7 +29,6 @@
"react-hook-form": "^7.52.0",
"react-router-dom": "^6.23.1",
"swr": "^2.2.5",
"zod": "^3.23.8",
"zustand": "^4.5.2"
},
"devDependencies": {
@ -9927,14 +9926,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zustand": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz",

View File

@ -31,7 +31,6 @@
"react-hook-form": "^7.52.0",
"react-router-dom": "^6.23.1",
"swr": "^2.2.5",
"zod": "^3.23.8",
"zustand": "^4.5.2"
},
"devDependencies": {

View File

@ -1,4 +1,4 @@
import { BrowserRouter as Router, Route, Routes, Navigate, redirect } from "react-router-dom"
import { BrowserRouter as Router, Route, Routes, Navigate } from "react-router-dom"
import Main from "./pages/Main"
import Users from "./pages/Users"
import Roles from "./pages/Roles"
@ -10,7 +10,7 @@ import ApiTest from "./pages/ApiTest"
import SignUp from "./pages/auth/SignUp"
import { initAuth, useAuthStore } from "./store/auth"
import { useEffect, useState } from "react"
import { Box, CircularProgress, Container } from "@mui/material"
import { Box, CircularProgress } from "@mui/material"
import Documents from "./pages/Documents"
import Reports from "./pages/Reports"

View File

@ -4,14 +4,13 @@ import Avatar from '@mui/material/Avatar';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import Settings from '@mui/icons-material/Settings';
import Logout from '@mui/icons-material/Logout';
import { useNavigate } from 'react-router-dom';
import { logout, useAuthStore } from '../store/auth';
import { ListItem, Switch, styled } from '@mui/material';
import { logout } from '../store/auth';
import { ListItemText, Switch, styled } from '@mui/material';
import { setDarkMode, usePrefStore } from '../store/preferences';
const Android12Switch = styled(Switch)(({ theme }) => ({
@ -79,36 +78,39 @@ export default function AccountMenu() {
</IconButton>
</Tooltip>
</Box>
<Menu
anchorEl={anchorEl}
id="account-menu"
open={open}
onClose={handleClose}
PaperProps={{
elevation: 0,
sx: {
overflow: 'visible',
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
mt: 1.5,
'& .MuiAvatar-root': {
width: 32,
height: 32,
ml: -0.5,
mr: 1,
slotProps={{
paper: {
elevation: 0,
sx: {
overflow: 'visible',
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
mt: 1.5,
'& .MuiAvatar-root': {
width: 32,
height: 32,
ml: -0.5,
mr: 1,
},
'&::before': {
content: '""',
display: 'block',
position: 'absolute',
top: 0,
right: 14,
width: 10,
height: 10,
bgcolor: 'background.paper',
transform: 'translateY(-50%) rotate(45deg)',
zIndex: 0,
},
},
'&::before': {
content: '""',
display: 'block',
position: 'absolute',
top: 0,
right: 14,
width: 10,
height: 10,
bgcolor: 'background.paper',
transform: 'translateY(-50%) rotate(45deg)',
zIndex: 0,
},
},
}
}}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
@ -116,12 +118,16 @@ export default function AccountMenu() {
<MenuItem onClick={() => {
console.log()
}}>
<Android12Switch
defaultChecked={prefStore.darkMode}
onChange={(e) => {
setDarkMode(e.target.checked)
}} />
Тема
<ListItemIcon>
<Android12Switch
defaultChecked={prefStore.darkMode}
onChange={(e) => {
setDarkMode(e.target.checked)
}} />
</ListItemIcon>
<ListItemText>
Тема: {prefStore.darkMode ? "темная" : "светлая"}
</ListItemText>
</MenuItem>

View File

@ -1,21 +1,21 @@
import { useState, useEffect, useMemo } from 'react';
import axiosInstance from '../http/axiosInstance';
import { useState, useEffect, useMemo } from 'react'
import axiosInstance from '../http/axiosInstance'
export function useDataFetching<T>(url: string, initData: T): T {
const [data, setData] = useState<T>(initData);
const [data, setData] = useState<T>(initData)
useEffect(() => {
const fetchData = async () => {
const response = await axiosInstance.get(url);
const result = await response.data;
setData(result);
};
const response = await axiosInstance.get(url)
const result = await response.data
setData(result)
}
fetchData();
}, [url]);
fetchData()
}, [url])
console.log(data)
// Memoize the data value
const memoizedData = useMemo<T>(() => data, [data]);
return memoizedData;
};
const memoizedData = useMemo<T>(() => data, [data])
return memoizedData
}
export default useDataFetching;

View File

@ -1,6 +1,6 @@
import { useDocuments, useDownload, useFolders } from '../hooks/swrHooks'
import { IDocument, IDocumentFolder } from '../interfaces/documents'
import { Box, Breadcrumbs, Button, Card, CardActionArea, CircularProgress, Divider, IconButton, Input, InputLabel, LinearProgress, Link, List, ListItem, ListItemButton, SxProps } from '@mui/material'
import { Box, Breadcrumbs, Button, CircularProgress, Divider, IconButton, Link, List, ListItemButton, SxProps } from '@mui/material'
import { Cancel, Close, Download, Folder, InsertDriveFile, Upload, UploadFile } from '@mui/icons-material'
import React, { useEffect, useRef, useState } from 'react'
import DocumentService from '../services/DocumentService'
@ -29,7 +29,7 @@ const FileItemStyle: SxProps = {
padding: '8px'
}
function ItemFolder({ folder, index, handleFolderClick, ...props }: FolderProps) {
function ItemFolder({ folder, handleFolderClick, ...props }: FolderProps) {
return (
<ListItemButton
onClick={() => handleFolderClick(folder)}
@ -118,6 +118,7 @@ export default function FolderViewer() {
}
const handleDocumentClick = async (doc: IDocument, index: number) => {
console.log(doc)
setCurrentFileNo(index)
setFileViewerModal(true)
}
@ -139,7 +140,7 @@ export default function FolderViewer() {
setDragOver(true)
}
const handleDragLeave = (e: React.DragEvent) => {
const handleDragLeave = () => {
setDragOver(false)
}
@ -159,11 +160,11 @@ export default function FolderViewer() {
setIsUploading(true)
if (filesToUpload.length > 0 && currentFolder && currentFolder.id) {
const formData = new FormData()
for (let file of filesToUpload) {
for (const file of filesToUpload) {
formData.append('files', file)
}
try {
const response = await DocumentService.uploadFiles(currentFolder.id, formData, setUploadProgress);
await DocumentService.uploadFiles(currentFolder.id, formData, setUploadProgress);
setIsUploading(false);
setFilesToUpload([]);
mutate(`/info/documents/${currentFolder.id}`);

View File

@ -1,4 +1,4 @@
import { memo, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import UserService from "../services/UserService";
export default function useUserData<T>(token: string, initData: T): T {

View File

@ -2,7 +2,7 @@ import { SubmitHandler, useForm } from 'react-hook-form';
import { AxiosResponse } from 'axios';
import { ApiResponse } from '../../interfaces/auth';
import RoleService from '../../services/RoleService';
import { Box, Button, MenuItem, Modal, Select, TextField, Typography } from '@mui/material';
import { Box, Button, Modal, Select, TextField, Typography } from '@mui/material';
import { ICompany } from '../../interfaces/documents';
import { useCompanies } from '../../hooks/swrHooks';
@ -12,7 +12,7 @@ interface Props {
}
const style = {
position: 'absolute' as 'absolute',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',

View File

@ -11,7 +11,7 @@ interface Props {
}
const style = {
position: 'absolute' as 'absolute',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',

View File

@ -11,7 +11,7 @@ interface Props {
}
const style = {
position: 'absolute' as 'absolute',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
@ -53,7 +53,6 @@ export default function CreateRoleModal({
aria-describedby="modal-modal-description"
>
<form onSubmit={handleSubmit(onSubmit)}>
<Box sx={style}>
<Typography variant="h6" component="h6" gutterBottom>
Создание роли

View File

@ -11,7 +11,7 @@ interface Props {
}
const style = {
position: 'absolute' as 'absolute',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',

View File

@ -1,8 +1,7 @@
import React, { useEffect, useRef, useState } from 'react'
import { useEffect, useRef } from 'react'
import { AppBar, Box, Button, CircularProgress, Dialog, IconButton, Toolbar, Typography } from '@mui/material';
import { ChevronLeft, ChevronRight, Close, Warning } from '@mui/icons-material';
import { useDownload, useFileType } from '../../hooks/swrHooks';
import { fileTypeFromBlob } from 'file-type/core';
import jsPreviewExcel from "@js-preview/excel"
import '@js-preview/excel/lib/index.css'
@ -11,11 +10,12 @@ import jsPreviewDocx from "@js-preview/docx"
import '@js-preview/docx/lib/index.css'
import jsPreviewPdf from '@js-preview/pdf'
import { IDocument } from '../../interfaces/documents';
interface Props {
open: boolean;
setOpen: (state: boolean) => void;
docs: any;
docs: IDocument[];
currentFileNo: number;
setCurrentFileNo: (state: number) => void;
}
@ -29,13 +29,10 @@ function PdfViewer({
}: ViewerProps) {
const previewContainerRef = useRef(null)
const [loadingPreview, setLoadingPreview] = useState(false)
const pdfPreviewer = jsPreviewPdf
useEffect(() => {
if (previewContainerRef && previewContainerRef.current) {
setLoadingPreview(true);
pdfPreviewer.init(previewContainerRef.current)
.preview(url)
}
@ -60,11 +57,8 @@ function DocxViewer({
}: ViewerProps) {
const previewContainerRef = useRef(null)
const [loadingPreview, setLoadingPreview] = useState(false)
useEffect(() => {
if (previewContainerRef && previewContainerRef.current) {
setLoadingPreview(true);
jsPreviewDocx.init(previewContainerRef.current, {
breakPages: true,
inWrapper: true,
@ -93,11 +87,8 @@ function ExcelViewer({
}: ViewerProps) {
const previewContainerRef = useRef(null)
const [loadingPreview, setLoadingPreview] = useState(false)
useEffect(() => {
if (previewContainerRef && previewContainerRef.current) {
setLoadingPreview(true);
jsPreviewExcel.init(previewContainerRef.current)
.preview(url)
}
@ -145,7 +136,7 @@ export default function FileViewer({
currentFileNo,
setCurrentFileNo
}: Props) {
const { file, isError, isLoading: fileIsLoading } = useDownload(currentFileNo >= 0 ? docs[currentFileNo]?.document_folder_id : null, currentFileNo >= 0 ? docs[currentFileNo]?.id : null)
const { file, isLoading: fileIsLoading } = useDownload(currentFileNo >= 0 ? docs[currentFileNo]?.document_folder_id : null, currentFileNo >= 0 ? docs[currentFileNo]?.id : null)
const { fileType, isLoading: fileTypeIsLoading } = useFileType(currentFileNo >= 0 ? docs[currentFileNo]?.name : null, currentFileNo >= 0 ? file : null)

View File

@ -83,10 +83,10 @@ export function useDownload(folder_id?: number | null, id?: number | null) {
}
}
export function useFileType(fileName?: string, file?: Blob) {
export function useFileType(fileName?: string | null, file?: Blob | null) {
const { data, error, isLoading } = useSWR(
fileName && file ? `/filetype/${fileName}` : null,
file ? (key: string) => fileTypeFromBlob(file) : null,
file ? () => fileTypeFromBlob(file) : null,
{
revalidateOnFocus: false
}
@ -134,7 +134,7 @@ export function useAddress(limit?: number, page?: number) {
}
}
export function useRegions(limit?: number, page?: number, search?: string) {
export function useRegions(limit?: number, page?: number, search?: string | null) {
const { data, error, isLoading } = useSWR(
`/general/regions?limit=${limit || 10}&page=${page || 1}${search ? `&search=${search}` : ''}`,
(url) => fetcher(url, BASE_URL.fuel),

View File

@ -19,7 +19,7 @@ export interface UserCreds extends User {
export interface AuthState {
isAuthenticated: boolean;
token: string | null;
userData: UserData | {};
userData: UserData | null;
}
export interface LoginFormData {
@ -32,8 +32,8 @@ export interface LoginFormData {
}
export interface ApiResponse {
access_token: any;
data: any;
access_token: string;
data: JSON;
status: number;
statusText: string;
}

View File

@ -2,3 +2,17 @@ export interface IRegion {
id: number;
name: string;
}
export interface ICity {
id: number;
name: string;
}
export interface IBoiler {
id: string;
id_object: string;
boiler_name: string;
boiler_code: string;
id_city: number;
activity: boolean;
}

View File

@ -4,6 +4,12 @@ export interface IServer {
region_id: number;
}
export interface IServersInfo extends IServer {
servers_count: number;
IPs_count: number;
status: string;
}
export interface IServerIP {
name: string;
is_actual: boolean;

View File

@ -9,17 +9,14 @@ import List from '@mui/material/List';
import Typography from '@mui/material/Typography';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import Badge from '@mui/material/Badge';
import Container from '@mui/material/Container';
import MenuIcon from '@mui/icons-material/Menu';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import NotificationsIcon from '@mui/icons-material/Notifications';
import { AccountCircle, Api, Assignment, ExitToApp, Home, People, Print, Report, Schedule, SendAndArchive, Settings, Shield, Storage, Tablet } from '@mui/icons-material';
import { ListItem, ListItemButton, ListItemIcon, ListItemText, colors } from '@mui/material';
import { Api, Assignment, Home, People, Shield, Storage, } from '@mui/icons-material';
import { ListItem, ListItemButton, ListItemIcon, ListItemText, } from '@mui/material';
import { Outlet, useNavigate } from 'react-router-dom';
import { UserData } from '../interfaces/auth';
import { getUserData, useAuthStore } from '../store/auth';
import { Theme, useTheme } from '@emotion/react';
import { useTheme } from '@emotion/react';
import AccountMenu from '../components/AccountMenu';
const drawerWidth: number = 240;

View File

@ -1,6 +1,6 @@
// Layout for dashboard with responsive drawer
import { Link, NavLink, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom"
import { Outlet, useLocation, useNavigate } from "react-router-dom"
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';

View File

@ -1,5 +1,5 @@
import "@fontsource/inter";
import React from 'react'
import React, { useEffect } from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
@ -7,9 +7,9 @@ import { registerSW } from 'virtual:pwa-register'
import { ThemeProvider } from '@emotion/react'
import { createTheme } from '@mui/material'
import { ruRU } from '@mui/material/locale'
import { usePrefStore } from "./store/preferences.ts";
import { getDarkMode, usePrefStore } from "./store/preferences.ts";
const theme = createTheme(
const darkTheme = createTheme(
{
typography: {
fontFamily: [
@ -29,6 +29,34 @@ const theme = createTheme(
}
},
palette: {
mode: "dark",
primary: { main: '#1976d2' },
},
},
ruRU,
);
const lightTheme = createTheme(
{
typography: {
fontFamily: [
'Inter'
].join(',')
},
components: {
MuiButtonBase: {
defaultProps: {
//disableRipple: true,
}
},
MuiButtonGroup: {
defaultProps: {
//disableRipple: true,
}
}
},
palette: {
mode: "light",
primary: { main: '#1976d2' },
},
},
@ -46,10 +74,22 @@ const updateSW = registerSW({
},
});
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ThemeProvider theme={theme}>
function ThemedApp() {
const prefStore = usePrefStore()
useEffect(() => {
getDarkMode()
}, [])
return (
<ThemeProvider theme={prefStore.darkMode ? darkTheme : lightTheme}>
<App />
</ThemeProvider>
)
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ThemedApp />
</React.StrictMode>,
)

View File

@ -1,14 +1,14 @@
import { Autocomplete, Box, Button, CircularProgress, Paper, TextField, Typography } from "@mui/material"
import { useBoilers, useCities, useRegions, useServers } from "../hooks/swrHooks"
import { Autocomplete, Box, CircularProgress, Paper, TextField, Typography } from "@mui/material"
import { useBoilers, useRegions, useServers } from "../hooks/swrHooks"
import { Fragment, useEffect, useState } from "react"
import { IRegion } from "../interfaces/fuel"
import { IBoiler, IRegion } from "../interfaces/fuel"
import { DataGrid, GridColDef } from "@mui/x-data-grid"
export default function ApiTest() {
const [open, setOpen] = useState(false)
const [options, setOptions] = useState<any>([])
const [search, setSearch] = useState<any>(null)
const [debouncedSearch, setDebouncedSearch] = useState("")
const [options, setOptions] = useState<IRegion[]>([])
const [search, setSearch] = useState<string | null>(null)
const [debouncedSearch, setDebouncedSearch] = useState<string | null>("")
const [selectedOption, setSelectedOption] = useState<IRegion | null>(null)
const { regions, isLoading } = useRegions(10, 1, debouncedSearch)
@ -28,15 +28,34 @@ export default function ApiTest() {
}
}, [regions])
const handleInputChange = (event: any, value: any) => {
const handleInputChange = (value: string) => {
setSearch(value)
}
const handleOptionChange = (event: any, value: any) => {
const handleOptionChange = (value: IRegion | null) => {
setSelectedOption(value)
}
//const { boilers } = useBoilers(10, 1)
const [boilersPage, setBoilersPage] = useState(1)
const [boilerSearch, setBoilerSearch] = useState("")
const [debouncedBoilerSearch, setDebouncedBoilerSearch] = useState("")
const { boilers } = useBoilers(10, boilersPage, debouncedBoilerSearch)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedBoilerSearch(boilerSearch)
}, 500)
return () => {
clearTimeout(handler)
}
}, [boilerSearch])
useEffect(() => {
setBoilersPage(1)
setBoilerSearch("")
}, [])
//const { cities } = useCities(10, 1)
const { servers, isLoading: serversLoading } = useServers(selectedOption?.id, 0, 10)
@ -45,12 +64,20 @@ export default function ApiTest() {
{ field: 'name', headerName: 'Название', type: "string" },
]
const boilersColumns: GridColDef[] = [
{ field: 'id', headerName: 'ID', type: "number" },
{ field: 'boiler_name', headerName: 'Название', type: "string" },
{ field: 'boiler_code', headerName: 'Код', type: "string" },
{ field: 'id_city', headerName: 'Город', type: "string" },
{ field: 'activity', headerName: 'Активен', type: "boolean" },
]
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
<Paper elevation={1}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%', p: '16px'}}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%', p: '16px' }}>
<Typography variant='h6' fontWeight='600'>
Get servers
Servers
</Typography>
<Autocomplete
@ -61,11 +88,11 @@ export default function ApiTest() {
onClose={() => {
setOpen(false)
}}
onInputChange={handleInputChange}
onChange={handleOptionChange}
onInputChange={(_, value) => handleInputChange(value)}
onChange={(_, value) => handleOptionChange(value)}
filterOptions={(x) => x}
isOptionEqualToValue={(option: any, value: any) => option.name === value.name}
getOptionLabel={(option: any) => option.name ? option.name : ""}
isOptionEqualToValue={(option: IRegion, value: IRegion) => option.name === value.name}
getOptionLabel={(option: IRegion) => option.name ? option.name : ""}
options={options}
loading={isLoading}
value={selectedOption}
@ -98,6 +125,24 @@ export default function ApiTest() {
}
</Box>
</Paper>
<Paper elevation={1}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%', p: '16px' }}>
<Typography variant='h6' fontWeight='600'>
Boilers
</Typography>
{boilers &&
<DataGrid
rows={boilers.map((boiler: IBoiler) => {
return { ...boiler, id: boiler.id_object }
})}
columns={boilersColumns}
/>
}
</Box>
</Paper>
</Box>
)
}

View File

@ -1,37 +1,6 @@
import { GridColDef } from '@mui/x-data-grid'
import FolderViewer from '../components/FolderViewer'
export default function Documents() {
const organizationColumns: GridColDef[] = [
{ field: 'id', headerName: 'ID', type: "number", width: 90 },
{ field: 'id_1c', headerName: 'ID 1C', type: "string", width: 90 },
{ field: 'name', headerName: 'Наименование', type: "string", width: 90 },
{ field: 'full_name', headerName: 'Полное наименование', type: "string", width: 90 },
{ field: 'inn', headerName: 'ИНН', type: "string", width: 70 },
{ field: 'ogrn', headerName: 'ОГРН', width: 130 },
{ field: 'kpp', headerName: 'КПП', width: 130 },
{ field: 'okopf', headerName: 'ОКОПФ', width: 90 },
{ field: 'legal_address', headerName: 'Юридический адрес', width: 90 },
{ field: 'actual_address', headerName: 'Фактический адрес', width: 90 },
{ field: 'mail_address', headerName: 'Почтовый адрес', type: "string", width: 90 },
{ field: 'id_budget', headerName: 'Активен', type: "number", width: 90 },
{ field: 'fio_dir', headerName: 'Активен', type: "string", width: 90 },
{ field: 'phone', headerName: 'Телефон', type: "string", width: 90 },
{ field: 'email', headerName: 'Email', type: "string", width: 90 },
{ field: 'comment', headerName: 'Комментарий', type: "string", width: 90 },
{ field: 'id_bank', headerName: 'Банк', type: "string", width: 90 },
{ field: 'active', headerName: 'Активен', type: "boolean", width: 90 },
];
const bankColumns: GridColDef[] = [
{ field: 'id', headerName: 'ID', type: "number", width: 90 },
{ field: 'id_1c', headerName: 'ID 1C', type: "string", width: 90 },
{ field: 'name', headerName: 'Наименование', type: "string", width: 90 },
{ field: 'bik', headerName: 'БИК', type: "string", width: 90 },
{ field: 'corschet', headerName: 'Кор. счет', type: "string", width: 70 },
{ field: 'activ', headerName: 'Активен', type: "boolean", width: 130 },
];
return (
<div>
<FolderViewer />

View File

@ -1,6 +1,5 @@
import { Error } from "@mui/icons-material";
import { Box, Typography, colors } from "@mui/material";
import { red } from "@mui/material/colors";
import { Box, Typography } from "@mui/material";
export default function NotFound() {
return (

View File

@ -1,18 +1,15 @@
import { useEffect, useState } from "react"
import { Box, Button, Typography } from "@mui/material"
import { useState } from "react"
import { Box, Button } from "@mui/material"
import axiosInstance from "../http/axiosInstance"
import { DataGrid } from "@mui/x-data-grid"
export default function Reports() {
const [state, setState] = useState<any>(null)
const [exportData, setExportData] = useState<any>(null)
const [state, setState] = useState(null)
const fetch = async () => {
await axiosInstance.get(`/info/reports/0?to_export=true`, {
responseType: 'blob',
}).then(response => {
setExportData(response.data)
const url = window.URL.createObjectURL(response.data)
const link = document.createElement('a')
link.href = url
@ -71,7 +68,7 @@ export default function Reports() {
disableRowSelectionOnClick
processRowUpdate={(updatedRow, originalRow) => {
console.log(updatedRow)
console.log(updatedRow, originalRow)
return updatedRow
}}

View File

@ -49,7 +49,7 @@ export default function Roles() {
disableRowSelectionOnClick
processRowUpdate={(updatedRow, originalRow) => {
console.log(updatedRow)
console.log(updatedRow, originalRow)
return updatedRow
}}

View File

@ -23,7 +23,7 @@ export default function Settings() {
{
field: 'role_id',
headerName: 'Роль',
valueGetter: (value, row) => {
valueGetter: (value) => {
if (roles) {
const roleName = roles.find((role: IRole) => role.id === value).name
return roleName
@ -70,7 +70,7 @@ export default function Settings() {
disableRowSelectionOnClick
processRowUpdate={(updatedRow, originalRow) => {
console.log(updatedRow)
console.log(updatedRow, originalRow)
return updatedRow
}}

View File

@ -65,7 +65,7 @@ export default function Users() {
disableRowSelectionOnClick
processRowUpdate={(updatedRow, originalRow) => {
console.log(updatedRow)
console.log(updatedRow, originalRow)
return updatedRow
}}

View File

@ -10,7 +10,7 @@ const config: AxiosRequestConfig = {
}
export default class AuthService {
static async login(data: any) {
static async login(data: FormData) {
return await axiosInstance.post(`/auth/login`, data, config)
}

View File

@ -100,11 +100,11 @@ export default class DocumentService {
}
// Upload Files
static async uploadFiles(folder_id: number, files: any, setUploadProgress?: any) {
static async uploadFiles(folder_id: number, files: FormData, setUploadProgress?: (value: number) => void) {
return await axiosInstance.post(`/info/documents/upload/${folder_id}`, files, {
onUploadProgress: (progressEvent: AxiosProgressEvent) => {
const percentCompleted = progressEvent.progress
setUploadProgress?.(percentCompleted)
setUploadProgress?.(percentCompleted || 0)
}
})
}
@ -127,7 +127,7 @@ export default class DocumentService {
}
// Convert Phones
static async convertPhones(data: any) {
static async convertPhones(data: FormData) {
return await axiosInstance.post(`/info/other/phones/`, data)
}

View File

@ -1,4 +1,4 @@
import axios, { AxiosRequestConfig } from "axios";
import { AxiosRequestConfig } from "axios";
import axiosInstance from "../http/axiosInstance";
import { IHardware, IServer, IServerIP, IStorage } from "../interfaces/servers";
import { BASE_URL } from "../constants";

View File

@ -2,13 +2,14 @@ import { AxiosRequestConfig } from "axios";
import axiosInstance from "../http/axiosInstance";
import { UserCreds, UserData } from "../interfaces/auth";
import { BASE_URL } from "../constants";
import { IUserCreate } from "../interfaces/user";
const config: AxiosRequestConfig = {
baseURL: BASE_URL.auth
}
export default class UserService {
static async createUser(data: any) {
static async createUser(data: IUserCreate) {
return await axiosInstance.post(`/auth/user`, data, config)
}

View File

@ -3,10 +3,10 @@ import { TOKEN_AUTH_KEY, TOKEN_EXPIRY_DURATION, TOKEN_ISSUED_DATE_KEY, USER_DATA
import { AuthState } from '../interfaces/auth';
import AuthService from '../services/AuthService';
export const useAuthStore = create<AuthState>((set, get) => ({
export const useAuthStore = create<AuthState>(() => ({
isAuthenticated: false,
token: null,
userData: {},
userData: null,
}));
const login = (token: string) => {
@ -20,7 +20,7 @@ const logout = () => {
localStorage.removeItem(TOKEN_AUTH_KEY);
localStorage.removeItem(USER_DATA_KEY);
localStorage.removeItem(TOKEN_ISSUED_DATE_KEY);
useAuthStore.setState(() => ({ isAuthenticated: false, token: null, userData: {} }));
useAuthStore.setState(() => ({ isAuthenticated: false, token: null, userData: null }));
}
const initAuth = async () => {

View File

@ -1,12 +1,13 @@
import { create } from 'zustand';
import { PreferencesState } from '../interfaces/preferences';
export const usePrefStore = create<PreferencesState>((set, get) => ({
export const usePrefStore = create<PreferencesState>(() => ({
darkMode: false
}));
const getDarkMode = () => {
const darkMode = localStorage.getItem('darkMode')
usePrefStore.setState(() => ({ darkMode: darkMode?.toLowerCase() === "true" ? true : false }))
return darkMode
}

File diff suppressed because one or more lines are too long

2
frontend_reactjs/vite.config.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;

View File

@ -0,0 +1,82 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import { VitePWA } from "vite-plugin-pwa";
import { nodePolyfills } from 'vite-plugin-node-polyfills';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
nodePolyfills(),
react(),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ["**/*"],
runtimeCaching: [
{
urlPattern: function (_a) {
var request = _a.request;
return request.mode === 'navigate';
},
handler: 'NetworkFirst',
options: {
cacheName: 'html-cache',
},
},
{
urlPattern: /\.(?:js|css)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-resources',
},
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
handler: 'CacheFirst',
options: {
cacheName: 'image-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
]
},
includeAssets: [
"**/*",
],
manifest: {
"theme_color": "#f69435",
"background_color": "#f69435",
"display": "standalone",
"scope": "/",
"start_url": "/",
"short_name": "Vite PWA",
"description": "Vite PWA Boilerplate",
"name": "Vite PWA Boilerplate",
"icons": [
{
"src": "/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
},
}),
],
});

View File

@ -5349,11 +5349,6 @@ yocto-queue@^0.1.0:
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zod@^3.23.8:
version "3.23.8"
resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
zustand@^4.5.2:
version "4.5.2"
resolved "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz"