diff --git a/src/components/UploadFileForm.jsx b/src/components/UploadFileForm.jsx new file mode 100644 index 0000000..831ff87 --- /dev/null +++ b/src/components/UploadFileForm.jsx @@ -0,0 +1,56 @@ +import { Form, Upload, Button} from 'antd' +import { UploadOutlined} from '@ant-design/icons' +import { useState } from 'react' +import { upload } from './factory' + +export default function UploadFileForm({url, onUploadStart, onUploadComplete, onUploadError}) { + const [isSubmitButtonEnabled, setSubmitButtonEnabled] = useState(false) + const [form] = Form.useForm(); + + const handleFileSend = async (values) => { + if(onUploadStart) + onUploadStart() + try { + const values = await form.validateFields(); + const formData = new FormData() + values.documentFile.fileList.forEach((val) => { + formData.append("files", val.originFileObj); + }); + await upload(url, formData) + } catch(error) { + if(onUploadError) + onUploadError(error) + } finally { + form.resetFields() + if(onUploadComplete) + onUploadComplete() + } + } + + return( +
+ + setSubmitButtonEnabled(props.fileList.length > 0)}> + + + + + + +
+ ) +} \ No newline at end of file diff --git a/src/components/modalWindows/DocumentCreationForm.jsx b/src/components/modalWindows/DocumentCreationForm.jsx deleted file mode 100644 index 53a6e7f..0000000 --- a/src/components/modalWindows/DocumentCreationForm.jsx +++ /dev/null @@ -1,108 +0,0 @@ -import { Form, Upload, Button } from 'antd' -import { InboxOutlined } from '@ant-design/icons' -import {useState} from "react" -import {useParams} from 'react-router-dom' - -const { Dragger } = Upload; - -export default function DocumentCreationForm({selectedFileCategory, closeModalHandler}){ - - let {id} = useParams() - - const [form] = Form.useForm(); - - const [selectedFiles, setSelectedFiles] = useState([]) - - const onFinish = (form) => { - form - .validateFields() - .then(values => { - var fileList = values.documentFile.fileList - - if (fileList.length > 0) { - const formData = new FormData(); - - fileList.forEach(val => { - formData.append('files', val.originFileObj) - }) - - fetch(`/api/files/${id}/files?idCategory=${selectedFileCategory}&idUser=${localStorage['userId']}`, { - headers: { - Authorization: 'Bearer ' + localStorage['token'] - }, - method: 'POST', - body: formData - }) - - form.resetFields(); - closeModalHandler() - } - }).catch(info => { - console.log('Validate Failed:', info); - }); - } - - const submitFileFormProps = { - progress: { - strokeColor: { - '0%': '#108ee9', - '100%': '#87d068', - }, - strokeWidth: 3, - format: percent => `${parseFloat(percent.toFixed(2))}%`, - }, - multiple: true, - onChange({ file, fileList }) { - if (file.status !== 'uploading') { - setSelectedFiles(fileList) - } - } - } - - return ( -
onFinish(form)} - > - - -

- -

-

Кликните или перенесите сюда файлы

-

- Возможна загрузка как одного файла, так и нескольких. -

-
-
- -
- - - -
-
-
- ) -} \ No newline at end of file diff --git a/src/pages/Documents.jsx b/src/pages/Documents.jsx deleted file mode 100644 index e4e6602..0000000 --- a/src/pages/Documents.jsx +++ /dev/null @@ -1,183 +0,0 @@ -import {useState, useEffect} from "react" -import {Table, DatePicker, Button, Modal, ConfigProvider} from 'antd' -import { UploadOutlined } from '@ant-design/icons' -import locale from "antd/lib/locale/ru_RU" -import moment from 'moment' -import DocumentCreationForm from '../components/modalWindows/DocumentCreationForm' -import { FileService } from '../services/api' -import notify from '../components/notify' -import LoaderPortal from '../components/LoaderPortal' - -const pageSize = 12 -const {RangePicker} = DatePicker; - -export default function Documents({selectedFileCategory, idWell}) { - const [page, setPage] = useState(1) - const [range, setRange] = useState([]) - const [pagination, setPagination] = useState(null) - const [files, setFiles] = useState([]) - const [isModalVisible, setIsModalVisible] = useState(false) - const [isTableUpdating, setTableUpdating] = useState(false) - - const [showLoader, setShowLoader] = useState(false) - - const handleFileNameCLick = async (event, row) => { - const element = event.target - - if(!element.href.length) { - try { - setShowLoader(true) - - await fetch(`/api/files/${idWell}/${row.id}`, { - headers: { - Authorization: 'Bearer ' + localStorage['token'] - } - }) - .then(async (response) => { - const blob = await response.blob(); - - let reader = new FileReader(); - reader.readAsDataURL(blob); - reader.onload = function (e) { - element.href = e.target.result - element.click() - }; - setShowLoader(false) - }); - } catch (error) { - notify(`Не удалось скачать файл ${row}`, 'error') - console.log(error) - } - } - } - - const columns = [ - { - title: 'Документ', - key: 'document', - dataIndex: 'name', - render: (name, row) => - - }, - { - title: 'Дата загрузки', - key: 'uploadDate', - dataIndex: 'uploadDate', - render: (item) => moment.utc(item).local().format('DD MMM YYYY, HH:mm:ss') - }, - { - title: 'Ф.И.О.', - key: 'userName', - dataIndex: 'userName', - } - ]; - - const openModal = () => { - setIsModalVisible(true) - } - - const closeModal = () => { - setIsModalVisible(false) - } - - const onChangeRange = (range) => { - setRange(range) - } - - useEffect(() => { - const GetDocuments = async () => { - setShowLoader(true) - - try { - let begin = null - let end = null - if (range?.length > 1) { - begin = range[0].toISOString() - end = range[1].toISOString() - } - - await FileService.getFilesInfo( - `${idWell}`, - (page - 1) * pageSize, - pageSize, - selectedFileCategory, - begin, - end - ).then((paginatedFiles) => { - setFiles(paginatedFiles?.items.map(f => { - return { - key: f.id, - begin: f.date, - ...f - } - })) - - setPagination({ - total: paginatedFiles?.count, - current: Math.floor(paginatedFiles?.skip / pageSize), - }) - - setTableUpdating(false) - setShowLoader(false) - } - ) - } catch (ex) { - notify(`Не удалось загрузить файлы по скважине "${idWell}"`, 'error') - console.log(ex) - } - } - GetDocuments() - }, [idWell, range, page, selectedFileCategory, isTableUpdating]) - - return ( -
-
-

Фильтр документов:

- - - -
- -
 
- - - -
- { - setIsModalVisible(false) - setTableUpdating(true) - }}> - -
-
-
 
- setPage(page) - }} - rowKey={(record) => record.id} - /> - ); -} \ No newline at end of file diff --git a/src/pages/Documents/DocumentsTemplate.jsx b/src/pages/Documents/DocumentsTemplate.jsx new file mode 100644 index 0000000..793d29e --- /dev/null +++ b/src/pages/Documents/DocumentsTemplate.jsx @@ -0,0 +1,111 @@ +import {useState, useEffect} from "react" +import {Table, DatePicker, Button, ConfigProvider} from 'antd' +import locale from "antd/lib/locale/ru_RU" +import moment from 'moment' +import { FileService } from '../../services/api' +import { updateFromWebApiWrapperAsync, download, makePaginationObject} from '../../components/factory' +import UploadFileForm from '../../components/UploadFileForm' +import LoaderPortal from '../../components/LoaderPortal' + +const pageSize = 12 +const {RangePicker} = DatePicker; + +export default function DocumentsTemplate({idCategory, idWell}) { + const [page, setPage] = useState(1) + const [range, setRange] = useState([]) + const [pagination, setPagination] = useState(null) + const [files, setFiles] = useState([]) + const [showLoader, setShowLoader] = useState(false) + + const uploadUrl = `/api/well/${idWell}/files/` + + const handleFileNameCLick = async (_, row) => { + updateFromWebApiWrapperAsync(async ()=>{ + await download(`/api/well/${idWell}/files/${row.id}`) + }, + setShowLoader, + `Не удалось скачать файл ${row}`) + } + + const columns = [ + { + title: 'Документ', + key: 'document', + dataIndex: 'name', + render: (name, row) => + + }, + { + title: 'Дата загрузки', + key: 'uploadDate', + dataIndex: 'uploadDate', + render: (item) => moment.utc(item).local().format('DD MMM YYYY, HH:mm:ss') + }, + { + title: 'Ф.И.О.', + key: 'userName', + dataIndex: 'userName', + } + ]; + + const addKeysAndUpdateFiles = (items) =>{ + const mappedFiles = items?.map(fileInfo => ({key: fileInfo.id, begin: fileInfo.date, ...fileInfo})) + setFiles(mappedFiles??[]) + } + + useEffect(() => { + let begin = null + let end = null + if (range?.length > 1) { + begin = range[0].toISOString() + end = range[1].toISOString() + } + + updateFromWebApiWrapperAsync( + async ()=>{ + const paginatedFiles = await FileService.getFilesInfo( + `${idWell}`, + (page - 1) * pageSize, + pageSize, + idCategory, + begin, + end) + if(!paginatedFiles) + return + addKeysAndUpdateFiles(paginatedFiles?.items) + + const newPagination = makePaginationObject(paginatedFiles) + setPagination(newPagination) + }, + setShowLoader, + `Не удалось загрузить файлы по скважине "${idWell}"`) + }, [idWell, range, page, idCategory]) + + return ( + + setShowLoader(true)} + onUploadComplete={()=>setShowLoader(false)} + /> +
+

Фильтр документов:

+ + + +
+
setPage(page)}} + rowKey={(record) => record.id} + /> + ); +} \ No newline at end of file diff --git a/src/pages/Documents/index.jsx b/src/pages/Documents/index.jsx new file mode 100644 index 0000000..aeff2f0 --- /dev/null +++ b/src/pages/Documents/index.jsx @@ -0,0 +1,27 @@ +import {Layout, Menu} from "antd"; +import {Switch, useParams} from "react-router-dom"; +import {makeMenuItems, makeRouteItems} from './menuItems' + +const { Content } = Layout + +export default function MenuDocuments({idWell}) { + let {category} = useParams() + const rootPath = `/well/${idWell}` + + return(<> + + {makeMenuItems('documentsPageMenu', rootPath)} + + + + + {makeRouteItems('documentsPageMenu', `${rootPath}`, idWell)} + + + + ) +} diff --git a/src/pages/Documents/menuItems.jsx b/src/pages/Documents/menuItems.jsx new file mode 100644 index 0000000..ef8aa8d --- /dev/null +++ b/src/pages/Documents/menuItems.jsx @@ -0,0 +1,58 @@ +import { Menu } from "antd"; +import { FolderOutlined } from "@ant-design/icons"; +import { Link, Route} from "react-router-dom"; +import DocumentsTemplate from './DocumentsTemplate' + +export const documentCategories = [ + {id:1, key:'fluidService', title:'Растворный сервис'}, + {id:2, key:'cementing', title:'Цементирование'}, + {id:3, key:'nnb', title:'ННБ'}, + {id:4, key:'gti', title:'ГТИ'}, + {id:5, key:'documentsForWell', title:'Документы по скважине'}, + {id:6, key:'supervisor', title:'Супервайзер'}, + {id:7, key:'master', title:'Мастер'}, +] + +const makeMenuItem = (keyPrefix, keyValue, rootPath, title, other) => ( + + {title} + ) + +const makeRouteItem = (keyPrefix, keyValue, rootPath, other) => ( + + + ) + +const formatRoutePath = (rootPath) =>{ + let root = rootPath.endsWith('/') + ? rootPath.slice(0,-1) + : rootPath + + root = root.endsWith('/document') + ? root + : `${root}/document` + + return root +} + +const getCategoriesByUserRole = (role) => documentCategories + .filter(cat => !cat.roles || cat.roles.includes('*') || cat.roles.includes(role)) + +export const makeMenuItems = (keyPrefix, rootPath) => { + const root = formatRoutePath(rootPath) + const categories = getCategoriesByUserRole(localStorage['roleName']) + return categories.map(category => + makeMenuItem(keyPrefix, category.key, root, category.title, {icon:})) +} + +export const makeRouteItems = (keyPrefix, rootPath, idWell) => { + const root = formatRoutePath(rootPath) + const categories = getCategoriesByUserRole(localStorage['roleName']) + const routes = categories.map(category => + makeRouteItem(keyPrefix, category.key, root, {idCategory: category.id, idWell: idWell})) + return routes; +} \ No newline at end of file diff --git a/src/pages/FluidService.jsx b/src/pages/FluidService.jsx deleted file mode 100644 index d75e422..0000000 --- a/src/pages/FluidService.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import Documents from "./Documents" - -export default function FluidService({selectedFileCategory}) { - - return( - - ) -} \ No newline at end of file diff --git a/src/pages/MenuDocuments.jsx b/src/pages/MenuDocuments.jsx deleted file mode 100644 index 12380f3..0000000 --- a/src/pages/MenuDocuments.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useState, useEffect } from "react"; -import {Layout, Menu} from "antd"; -import {FolderOutlined} from "@ant-design/icons"; -import {Link, Route, Switch, useLocation} from "react-router-dom"; -import Documents from "./Documents"; -import LastData from './LastData' - -const { Content } = Layout - -export default function MenuDocuments({idWell}) { - let currentPath = useLocation().pathname - - const rootPath = `/well/${idWell}` - - const [selectedElement, setSelectedElement] = useState('fluidService') - - useEffect(() => { - let pathLastElement = currentPath.split('/').pop() - setSelectedElement(pathLastElement) - }, [currentPath]) - - return( - <> - - }> - Растворный сервис - - }> - Цементирование - - }> - ННБ - - }> - ГТИ - - }> - Документы по скважине - - }> - Супервайзер - - }> - Мастер - - }> - Последние данные - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/pages/Smbo/EquipmentDetails.jsx b/src/pages/Smbo/EquipmentDetails.jsx index 15421a3..5178236 100644 --- a/src/pages/Smbo/EquipmentDetails.jsx +++ b/src/pages/Smbo/EquipmentDetails.jsx @@ -1,5 +1,5 @@ import {Row, Col} from 'antd' -import Documents from '../Documents' +import Documents from '../Documents/DocumentsTemplate' import '../../styles/equipment_details.css' export default function EquipmentDetails({id, equipmentTimers, equipmentSensors}) { diff --git a/src/pages/TelemetryView/CustomColumn.jsx b/src/pages/TelemetryView/CustomColumn.jsx index 0b49bd2..ed90fce 100644 --- a/src/pages/TelemetryView/CustomColumn.jsx +++ b/src/pages/TelemetryView/CustomColumn.jsx @@ -1,5 +1,5 @@ import {Display} from '../../components/Display' -import RigMnemo from '../../components/RigMnemo' +import RigMnemo from './RigMnemo' const params = [ {label:'Рот., об/мин', accessorName:'rotorSpeed', isArrowVisible:true}, diff --git a/src/components/RigMnemo.jsx b/src/pages/TelemetryView/RigMnemo.jsx similarity index 100% rename from src/components/RigMnemo.jsx rename to src/pages/TelemetryView/RigMnemo.jsx diff --git a/src/pages/Well.jsx b/src/pages/Well.jsx index d74f0be..650fabd 100644 --- a/src/pages/Well.jsx +++ b/src/pages/Well.jsx @@ -1,124 +1,116 @@ -import {Layout, Menu} from "antd"; -import {FolderOutlined, FundViewOutlined} from "@ant-design/icons"; -import {Link, Redirect, Route, Switch, useParams} from "react-router-dom"; +import { Layout, Menu } from "antd"; +import { FolderOutlined, FundViewOutlined } from "@ant-design/icons"; +import { Link, Redirect, Route, Switch, useParams } from "react-router-dom"; import TelemetryView from "./TelemetryView"; import Messages from "../pages/Messages"; import Report from "../pages/Report"; import Archive from "../pages/Archive"; import Analysis from "../pages/Analysis"; import WellAnalysis from "../pages/WellAnalysis"; -import MenuDocuments from "./MenuDocuments"; -import WellStat from "./WellStat" -import Smbo from "./Smbo" +import Documents from "../pages/Documents"; +import LastData from '../pages/LastData' +import { makeMenuItems } from "./Documents/menuItems"; +import WellStat from "./WellStat"; +import Smbo from "./Smbo"; -const { Content } = Layout +const { Content } = Layout; export default function Well() { - let { idWell } = useParams() - const rootPath = `/well/${idWell}` + let { idWell } = useParams(); + const rootPath = `/well/${idWell}`; - const {SubMenu} = Menu + const { SubMenu } = Menu; - return (<> - - - }> - Мониторинг - - }> - Сообщения - - }> - Рапорт - - }> - Анализ - - }> - Операции по скважине - - }> - Статистика - - }> - Архив - - + + + }> + Мониторинг + + }> + Сообщения + + }> + Рапорт + + }> + Анализ + + }> + + Операции по скважине + + + }> + Статистика + + }> + Архив + + Документы} - icon={} + title={ + + Документы + + } + icon={} selectable={true} > - }> - Растворный сервис - - }> - Цементирование - - }> - ННБ - - }> - ГТИ - - }> - Документы по скважине - - }> - Супервайзер - - }> - Мастер - - }> - Последние данные - + {makeMenuItems('documentsSubMenu', `${rootPath}/document`)} - }> - СМБО - - + }> + СМБО + + }> + Последние данные + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - ) + + ); }