Browse Source

upstream to API changes

mantine
cracklesparkle 11 months ago
parent
commit
704276037c
  1. 1
      frontend_reactjs/src/components/DataTable.tsx
  2. 191
      frontend_reactjs/src/components/FolderViewer.tsx
  3. 128
      frontend_reactjs/src/components/modals/CreateCompanyModal.tsx
  4. 109
      frontend_reactjs/src/components/modals/CreateDepartmentModal.tsx
  5. 43
      frontend_reactjs/src/hooks/swrHooks.ts
  6. 2
      frontend_reactjs/src/http/axiosInstance.ts
  7. 5
      frontend_reactjs/src/interfaces/documents.ts
  8. 83
      frontend_reactjs/src/pages/Documents.tsx
  9. 17
      frontend_reactjs/src/services/DocumentService.ts

1
frontend_reactjs/src/components/DataTable.tsx

@ -6,7 +6,6 @@ interface Props {
}
export default function DataTable(props: Props) {
return (
<DataGrid
autoHeight

191
frontend_reactjs/src/components/FolderViewer.tsx

@ -0,0 +1,191 @@
import { useDocuments, useFolders } from '../hooks/swrHooks'
import { IDocument, IDocumentFolder } from '../interfaces/documents'
import { Box, Breadcrumbs, Button, Card, CardActionArea, CircularProgress, LinearProgress, Link } from '@mui/material'
import { Folder, InsertDriveFile, Upload, UploadFile } from '@mui/icons-material'
import { useState } from 'react'
import DocumentService from '../services/DocumentService'
interface FolderProps {
folder: IDocumentFolder;
handleFolderClick: (folder: IDocumentFolder) => void;
}
function ItemFolder({ folder, handleFolderClick, ...props }: FolderProps) {
return (
<Card
key={folder.id}
>
<CardActionArea>
<Box
sx={{
cursor: 'pointer',
display: 'flex',
flexDirection: 'row',
gap: '8px',
alignItems: 'center',
padding: '8px'
}}
onClick={() => handleFolderClick(folder)}
{...props}
>
<Folder />
{folder.name}
</Box>
</CardActionArea>
</Card>
)
}
interface DocumentProps {
doc: IDocument;
handleDocumentClick: (doc: IDocument) => void;
}
function ItemDocument({ doc, handleDocumentClick, ...props }: DocumentProps) {
return (
<Card
key={doc.id}
>
<CardActionArea>
<Box
sx={{
cursor: 'pointer',
display: 'flex',
flexDirection: 'row',
gap: '8px',
alignItems: 'center',
padding: '8px',
}}
onClick={() => {
handleDocumentClick(doc)
}}
{...props}
>
<InsertDriveFile />
{doc.name}
</Box>
</CardActionArea>
</Card>
)
}
export default function FolderViewer() {
const [currentFolder, setCurrentFolder] = useState<IDocumentFolder | null>(null)
const [breadcrumbs, setBreadcrumbs] = useState<IDocumentFolder[]>([])
const { folders, isLoading: foldersLoading } = useFolders()
const { documents, isLoading: documentsLoading } = useDocuments(currentFolder?.id)
const handleFolderClick = (folder: IDocumentFolder) => {
setCurrentFolder(folder)
setBreadcrumbs((prev) => [...prev, folder])
}
const handleDocumentClick = async (doc: IDocument) => {
try {
const response = await DocumentService.downloadDoc(doc.document_folder_id, doc.id);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', doc.name);
document.body.appendChild(link);
link.click();
link.remove();
} catch (error) {
console.error(error);
}
}
const handleBreadcrumbClick = (index: number) => {
const newBreadcrumbs = breadcrumbs.slice(0, index + 1);
setBreadcrumbs(newBreadcrumbs)
setCurrentFolder(newBreadcrumbs[newBreadcrumbs.length - 1])
}
// const handleFileUpload = (event) => {
// const file = event.target.files[0]
// }
if (foldersLoading || documentsLoading) {
return (
<CircularProgress />
)
}
return (
<Box sx={{
display: 'flex',
flexDirection: 'column',
gap: '24px'
}}>
<Breadcrumbs>
<Link
underline='hover'
color='inherit'
onClick={() => {
setCurrentFolder(null)
setBreadcrumbs([])
}}
sx={{ cursor: 'pointer' }}
>
Главная
</Link>
{breadcrumbs.map((breadcrumb, index) => (
<Link
key={breadcrumb.id}
underline="hover"
color="inherit"
onClick={() => handleBreadcrumbClick(index)}
sx={{ cursor: 'pointer' }}
>
{breadcrumb.name}
</Link>
))}
</Breadcrumbs>
<Box>
{currentFolder &&
<Button
variant="outlined"
startIcon={<UploadFile />}
onClick={() => {
console.log(currentFolder.id)
}}
>
Загрузить
</Button>
}
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
flexWrap: 'wrap',
gap: '16px'
}}>
{currentFolder ? (
documents?.map((doc: IDocument) => (
<ItemDocument
key={`doc-${doc.id}`}
doc={doc}
handleDocumentClick={handleDocumentClick}
/>
))
) : (
folders?.map((folder: IDocumentFolder) => (
<ItemFolder
key={`folder-${folder.id}`}
folder={folder}
handleFolderClick={handleFolderClick}
/>
))
)}
</Box>
</Box>
)
}

128
frontend_reactjs/src/components/modals/CreateCompanyModal.tsx

@ -0,0 +1,128 @@
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 { ICompany } from '../../interfaces/documents';
import { useCompanies } from '../../hooks/swrHooks';
interface Props {
open: boolean;
setOpen: (state: boolean) => void;
}
const style = {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
boxShadow: 24,
borderRadius: 2,
p: 4,
display: "flex",
flexDirection: "column",
gap: "8px"
}
export default function CreateCompanyModal({
open,
setOpen
}: Props) {
const { companies } = useCompanies()
const { register, handleSubmit, formState: { errors } } = useForm<ICompany>({
defaultValues: {
name: '',
fullname: '',
description: '',
}
})
const onSubmit: SubmitHandler<ICompany> = async (data) => {
try {
const response: AxiosResponse<ApiResponse> = await RoleService.createRole(data)
console.log(response.data)
} catch (error) {
console.error(error)
}
}
return (
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<form onSubmit={handleSubmit(onSubmit)}>
<Box sx={style}>
<Typography variant="h6" component="h6" gutterBottom>
Создание компании
</Typography>
<TextField
fullWidth
margin="normal"
label="Наименование"
required
{...register('name', { required: 'Наименование обязательно' })}
error={!!errors.name}
helperText={errors.name?.message}
/>
<TextField
fullWidth
margin="normal"
label="Полное наименование"
required
{...register('fullname', { required: 'Полное наименование обязательно' })}
error={!!errors.fullname}
helperText={errors.fullname?.message}
/>
<TextField
fullWidth
margin="normal"
label="Описание"
{...register('description')}
error={!!errors.description}
helperText={errors.description?.message}
/>
{companies}
<Select
label="owner_id"
{...register('owner_id', { required: '' })}
error={!!errors.owner_id}
>
{/* {companies &&
companies.map((item, index) => {
<MenuItem>
{index}
</MenuItem>
})
} */}
</Select>
<Box sx={{
display: "flex",
justifyContent: "space-between",
gap: "8px"
}}>
<Button type="submit" variant="contained" color="primary">
Добавить роль
</Button>
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
Отмена
</Button>
</Box>
</Box>
</form>
</Modal>
)
}

109
frontend_reactjs/src/components/modals/CreateDepartmentModal.tsx

@ -0,0 +1,109 @@
import { SubmitHandler, useForm } from 'react-hook-form';
import { AxiosResponse } from 'axios';
import { ApiResponse } from '../../interfaces/auth';
import RoleService from '../../services/RoleService';
import { Box, Button, Modal, TextField, Typography } from '@mui/material';
import { ICompany } from '../../interfaces/documents';
interface Props {
open: boolean;
setOpen: (state: boolean) => void;
}
const style = {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
boxShadow: 24,
borderRadius: 2,
p: 4,
display: "flex",
flexDirection: "column",
gap: "8px"
}
export default function CreateDepartmentModal({
open,
setOpen
}: Props) {
const { register, handleSubmit, formState: { errors } } = useForm<ICompany>({
defaultValues: {
name: '',
fullname: '',
description: '',
}
})
const onSubmit: SubmitHandler<ICompany> = async (data) => {
try {
const response: AxiosResponse<ApiResponse> = await RoleService.createRole(data)
console.log(response.data)
} catch (error) {
console.error(error)
}
}
return (
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<form onSubmit={handleSubmit(onSubmit)}>
<Box sx={style}>
<Typography variant="h6" component="h6" gutterBottom>
Создание компании
</Typography>
<TextField
fullWidth
margin="normal"
label="Наименование"
required
{...register('name', { required: 'Наименование обязательно' })}
error={!!errors.name}
helperText={errors.name?.message}
/>
<TextField
fullWidth
margin="normal"
label="Полное наименование"
required
{...register('fullname', { required: 'Полное наименование обязательно' })}
error={!!errors.fullname}
helperText={errors.fullname?.message}
/>
<TextField
fullWidth
margin="normal"
label="Описание"
{...register('description')}
error={!!errors.description}
helperText={errors.description?.message}
/>
<Box sx={{
display: "flex",
justifyContent: "space-between",
gap: "8px"
}}>
<Button type="submit" variant="contained" color="primary">
Добавить роль
</Button>
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
Отмена
</Button>
</Box>
</Box>
</form>
</Modal>
)
}

43
frontend_reactjs/src/hooks/swrHooks.ts

@ -1,6 +1,7 @@
import useSWR from "swr";
import RoleService from "../services/RoleService";
import UserService from "../services/UserService";
import { fetcher } from "../http/axiosInstance";
export function useRoles() {
const { data, error, isLoading } = useSWR(`/auth/roles`, RoleService.getRoles)
@ -20,4 +21,46 @@ export function useUsers() {
isLoading,
isError: error
}
}
export function useCompanies(limit?: number, offset?: number) {
const { data, error, isLoading } = useSWR(`/info/companies?limit=${limit || 10}&offset=${offset || 0}`, fetcher)
return {
companies: data,
isLoading,
isError: error
}
}
export function useFolders(limit?: number, offset?: number) {
const { data, error, isLoading } = useSWR(
`/info/document_folder?limit=${limit || 10}&offset=${offset || 0}`,
fetcher,
{
revalidateOnFocus: false
}
)
return {
folders: data,
isLoading,
isError: error
}
}
export function useDocuments(folder_id?: number) {
const { data, error, isLoading } = useSWR(
folder_id ? `/info/documents/${folder_id}` : null,
fetcher,
{
revalidateOnFocus: false
}
)
return {
documents: data,
isLoading,
isError: error
}
}

2
frontend_reactjs/src/http/axiosInstance.ts

@ -18,4 +18,6 @@ axiosInstance.interceptors.request.use(
}
);
export const fetcher = (url: string) => axiosInstance.get(url).then(res => res.data)
export default axiosInstance;

5
frontend_reactjs/src/interfaces/documents.ts

@ -1,3 +1,4 @@
// owner_id relates to other companies
export interface ICompany {
name: string;
fullname: string;
@ -14,13 +15,15 @@ export interface IDepartment {
}
export interface IDocumentFolder {
id: number;
name: string;
description: string;
department_id: number;
create_date: string;
}
export interface IDocument {
id: number;
document_folder_id: number,
name: string;
description: string;
department_id: number;

83
frontend_reactjs/src/pages/Documents.tsx

@ -1,41 +1,7 @@
import React, { useState } from 'react'
import DocumentService from '../services/DocumentService'
import { Box, Button, Typography } from '@mui/material'
import { GridColDef } from '@mui/x-data-grid'
import DataTable from '../components/DataTable'
import FolderViewer from '../components/FolderViewer'
export default function Documents() {
const [companies, setCompanies] = useState()
const [departments, setDepartments] = useState()
const [organizations, setOrganizations] = useState()
const [banks, setBanks] = useState()
const [documents, setDocuments] = useState()
const fetchData = async () => {
await DocumentService.getCompanies().then(response => {
setCompanies(response.data)
})
await DocumentService.getOrganizations().then(response => {
setOrganizations(response.data)
})
await DocumentService.getDepartments().then(response => {
setDepartments(response.data)
})
await DocumentService.getBanks().then(response => {
setBanks(response.data)
})
}
const fetchDocuments = async () => {
await DocumentService.getDocuments().then(response => {
setDocuments(response.data)
})
}
const organizationColumns: GridColDef[] = [
{ field: 'id', headerName: 'ID', type: "number", width: 90 },
{ field: 'id_1c', headerName: 'ID 1C', type: "string", width: 90 },
@ -68,52 +34,7 @@ export default function Documents() {
return (
<div>
<Button onClick={() => {
fetchData()
}}>
Get data
</Button>
<Box>
<Typography variant='h6'>
Companies
</Typography>
{JSON.stringify(companies)}
</Box>
<Box>
<Typography variant='h6'>
Organizations
</Typography>
{organizations &&
<DataTable columns={organizationColumns} rows={organizations} />
}
</Box>
<Box>
<Typography variant='h6'>
Departments
</Typography>
{JSON.stringify(departments)}
</Box>
<Box>
<Typography variant='h6'>
Get banks
</Typography>
{banks &&
<DataTable columns={bankColumns} rows={banks} />
}
</Box>
<Button onClick={() => {
fetchDocuments()
}}>
Get documents
</Button>
{JSON.stringify(documents)}
<FolderViewer />
</div>
)
}

17
frontend_reactjs/src/services/DocumentService.ts

@ -1,3 +1,4 @@
import { AxiosProgressEvent } from "axios";
import axiosInstance from "../http/axiosInstance";
import { IBank, ICompany, IDepartment, IDocument, IDocumentFolder, IOrganization, IOrganizationBank } from "../interfaces/documents";
@ -99,17 +100,19 @@ export default class DocumentService {
}
// Upload Files
static async uploadFiles(folder_id: number, files: any) {
return await axiosInstance.post(`/info/documents/upload/${folder_id}`, files)
static async uploadFiles(folder_id: number, files: any, setUploadProgress?: any) {
return await axiosInstance.post(`/info/documents/upload/${folder_id}`, files, {
onUploadProgress: (progressEvent: AxiosProgressEvent) => {
const percentCompleted = progressEvent.progress
setUploadProgress?.(percentCompleted)
}
})
}
// Download Doc
static async downloadDoc(folder_id: number, doc_id: number) {
return await axiosInstance.get(`/info/document/`, {
params: {
folder_id: folder_id,
doc_id: doc_id
}
return await axiosInstance.get(`/info/document/${folder_id}&${doc_id}`, {
responseType: 'blob',
})
}

|||||||
100:0
Loading…
Cancel
Save