forked from ddrilling/asb_cloud_front
* Вывод ошибки о существовании рапорта исправлен
* Добавлены константы для mime-типов * Исправлена ошибка отправки файлов на сервер * Добавлен проп mimeType в UploadForm для контроля типов загружаемых файлов * Добавлена проверка необходимости обновления страница после закрытия окна редактирования части ПБ * Добавлен тип сообщения для notify * Изменён формат даты в окне суточного рапорта * Добавлен вывод сообщения исключения в редактировании суточного рапорта
This commit is contained in:
parent
3912e9b308
commit
12a6d96a95
@ -1,15 +1,17 @@
|
|||||||
import { memo, useCallback, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { Upload, Button } from 'antd'
|
import { Upload, Button } from 'antd'
|
||||||
import { UploadOutlined } from '@ant-design/icons'
|
import { UploadOutlined } from '@ant-design/icons'
|
||||||
import { UploadFile } from 'antd/lib/upload/interface'
|
import { UploadFile } from 'antd/lib/upload/interface'
|
||||||
|
import { RcFile } from 'antd/lib/upload'
|
||||||
|
|
||||||
import { upload } from './factory'
|
import { notify, upload } from './factory'
|
||||||
import { ErrorFetch } from './ErrorFetch'
|
import { ErrorFetch } from './ErrorFetch'
|
||||||
|
|
||||||
export type UploadFormProps = {
|
export type UploadFormProps = {
|
||||||
url: string
|
url: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
accept?: string
|
accept?: string
|
||||||
|
mimeTypes?: string | string[]
|
||||||
style?: CSSStyleSheet
|
style?: CSSStyleSheet
|
||||||
formData: FormData
|
formData: FormData
|
||||||
onUploadStart?: () => void
|
onUploadStart?: () => void
|
||||||
@ -18,14 +20,25 @@ export type UploadFormProps = {
|
|||||||
onUploadError?: (error: unknown) => void
|
onUploadError?: (error: unknown) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadForm = memo<UploadFormProps>(({ url, disabled, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError }) => {
|
export const UploadForm = memo<UploadFormProps>(({ url, disabled, style, formData, mimeTypes, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError }) => {
|
||||||
const [fileList, setfileList] = useState<UploadFile<any>[]>([])
|
const [fileList, setfileList] = useState<UploadFile<any>[]>([])
|
||||||
|
|
||||||
|
const checkMimeTypes = useCallback((file: RcFile) => {
|
||||||
|
const isAccepted = !mimeTypes || mimeTypes.includes(file.type)
|
||||||
|
if (isAccepted) return false
|
||||||
|
notify(`"${file.name}" является файлом неподходящего типа`, 'error')
|
||||||
|
return Upload.LIST_IGNORE
|
||||||
|
}, [mimeTypes])
|
||||||
|
|
||||||
|
const accept = useMemo(() => Array.isArray(mimeTypes) ? mimeTypes.join(',') : mimeTypes, [mimeTypes])
|
||||||
|
|
||||||
|
useEffect(() => console.log(fileList), [fileList])
|
||||||
|
|
||||||
const handleFileSend = useCallback(async () => {
|
const handleFileSend = useCallback(async () => {
|
||||||
onUploadStart?.()
|
onUploadStart?.()
|
||||||
try {
|
try {
|
||||||
const formDataLocal = new FormData()
|
const formDataLocal = new FormData()
|
||||||
fileList.forEach((val) => formDataLocal.append('files', String(val.originFileObj)))
|
fileList.forEach((val) => formDataLocal.append('files', val.originFileObj as Blob))
|
||||||
|
|
||||||
if(formData)
|
if(formData)
|
||||||
for(const propName in formData)
|
for(const propName in formData)
|
||||||
@ -58,6 +71,7 @@ export const UploadForm = memo<UploadFormProps>(({ url, disabled, accept, style,
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
fileList={fileList}
|
fileList={fileList}
|
||||||
onChange={(props) => setfileList(props.fileList)}
|
onChange={(props) => setfileList(props.fileList)}
|
||||||
|
beforeUpload={checkMimeTypes}
|
||||||
>
|
>
|
||||||
<Button disabled={disabled} icon={<UploadOutlined/>}>Загрузить файл</Button>
|
<Button disabled={disabled} icon={<UploadOutlined/>}>Загрузить файл</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
|
@ -11,12 +11,14 @@ const notificationTypeDictionary = new Map([
|
|||||||
['open' , { notifyInstance: notification.info , caption: '' }],
|
['open' , { notifyInstance: notification.info , caption: '' }],
|
||||||
])
|
])
|
||||||
|
|
||||||
|
export type NotifyType = 'error' | 'warning' | 'info'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Вызов оповещений всплывающим окошком.
|
* Вызов оповещений всплывающим окошком.
|
||||||
* @param body string или ReactNode
|
* @param body string или ReactNode
|
||||||
* @param notifyType для параметра типа. Допустимые значение 'error', 'warning', 'info'
|
* @param notifyType для параметра типа. Допустимые значение 'error', 'warning', 'info'
|
||||||
*/
|
*/
|
||||||
export const notify = (body: ReactNode, notifyType: string = 'info', other?: any) => {
|
export const notify = (body: ReactNode, notifyType: NotifyType = 'info', other?: any) => {
|
||||||
if (!body) return
|
if (!body) return
|
||||||
|
|
||||||
const instance = notificationTypeDictionary.get(notifyType) ??
|
const instance = notificationTypeDictionary.get(notifyType) ??
|
||||||
|
@ -31,7 +31,7 @@ const columns = [
|
|||||||
makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> })
|
makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> })
|
||||||
]
|
]
|
||||||
|
|
||||||
export const DocumentsTemplate = ({ idCategory, idWell: wellId, accept, headerChild, customColumns, beforeTable, onChange, tableName }) => {
|
export const DocumentsTemplate = ({ idCategory, idWell: wellId, mimeTypes, headerChild, customColumns, beforeTable, onChange, tableName }) => {
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [filterDataRange, setFilterDataRange] = useState([])
|
const [filterDataRange, setFilterDataRange] = useState([])
|
||||||
const [filterCompanyName, setFilterCompanyName] = useState([])
|
const [filterCompanyName, setFilterCompanyName] = useState([])
|
||||||
@ -134,7 +134,7 @@ export const DocumentsTemplate = ({ idCategory, idWell: wellId, accept, headerCh
|
|||||||
<span>Загрузка</span>
|
<span>Загрузка</span>
|
||||||
<UploadForm
|
<UploadForm
|
||||||
url={uploadUrl}
|
url={uploadUrl}
|
||||||
accept={accept}
|
mimeTypes={mimeTypes}
|
||||||
onUploadStart={() => setShowLoader(true)}
|
onUploadStart={() => setShowLoader(true)}
|
||||||
onUploadComplete={update}
|
onUploadComplete={update}
|
||||||
/>
|
/>
|
||||||
|
@ -28,9 +28,12 @@ export const CategoryEditor = memo(({ visible, category, onClosed }) => {
|
|||||||
const [showLoader, setShowLoader] = useState(false)
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
const [subject, setSubject] = useState(null)
|
const [subject, setSubject] = useState(null)
|
||||||
|
const [needUpdate, setNeedUpdate] = useState(false)
|
||||||
|
|
||||||
const idWell = useContext(IdWellContext)
|
const idWell = useContext(IdWellContext)
|
||||||
|
|
||||||
|
useEffect(() => visible && setNeedUpdate(false), [visible])
|
||||||
|
|
||||||
useEffect(() => invokeWebApiWrapperAsync(
|
useEffect(() => invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
const filteredUsers = users.filter(({ user }) => user && [
|
const filteredUsers = users.filter(({ user }) => user && [
|
||||||
@ -108,6 +111,7 @@ export const CategoryEditor = memo(({ visible, category, onClosed }) => {
|
|||||||
prevUsers[userIdx].status = status
|
prevUsers[userIdx].status = status
|
||||||
return [...prevUsers]
|
return [...prevUsers]
|
||||||
})
|
})
|
||||||
|
setNeedUpdate(true)
|
||||||
},
|
},
|
||||||
setShowLoader,
|
setShowLoader,
|
||||||
<>
|
<>
|
||||||
@ -138,9 +142,9 @@ export const CategoryEditor = memo(({ visible, category, onClosed }) => {
|
|||||||
const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject])
|
const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject])
|
||||||
|
|
||||||
const onModalClosed = useCallback(() => {
|
const onModalClosed = useCallback(() => {
|
||||||
onClosed?.()
|
onClosed?.(needUpdate)
|
||||||
calcUsers()
|
calcUsers()
|
||||||
}, [onClosed, calcUsers])
|
}, [onClosed, calcUsers, needUpdate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -14,7 +14,7 @@ import LoaderPortal from '@components/LoaderPortal'
|
|||||||
import Poprompt from '@components/selectors/Poprompt'
|
import Poprompt from '@components/selectors/Poprompt'
|
||||||
import { formatBytes, invokeWebApiWrapperAsync, notify } from '@components/factory'
|
import { formatBytes, invokeWebApiWrapperAsync, notify } from '@components/factory'
|
||||||
import { DrillingProgramService } from '@api'
|
import { DrillingProgramService } from '@api'
|
||||||
import { formatDate } from '@utils'
|
import { formatDate, MimeTypes } from '@utils'
|
||||||
|
|
||||||
import MarksCard from './MarksCard'
|
import MarksCard from './MarksCard'
|
||||||
|
|
||||||
@ -124,6 +124,7 @@ export const CategoryRender = memo(({ partData, onUpdate, onEdit, onHistory, set
|
|||||||
{permissionToUpload && (
|
{permissionToUpload && (
|
||||||
<UploadForm
|
<UploadForm
|
||||||
url={uploadUrl}
|
url={uploadUrl}
|
||||||
|
mimeTypes={MimeTypes.XLSX}
|
||||||
style={{ margin: '5px 0 10px 0' }}
|
style={{ margin: '5px 0 10px 0' }}
|
||||||
onUploadStart={() => setIsUploading(true)}
|
onUploadStart={() => setIsUploading(true)}
|
||||||
onUploadComplete={onUploadComplete}
|
onUploadComplete={onUploadComplete}
|
||||||
|
@ -8,9 +8,9 @@ import {
|
|||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
WarningOutlined,
|
WarningOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { IdWellContext } from '@asb/context'
|
import { useIdWell } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
|
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { arrayOrDefault, formatDate } from '@utils'
|
import { arrayOrDefault, formatDate } from '@utils'
|
||||||
@ -23,20 +23,22 @@ import CategoryHistory from './CategoryHistory'
|
|||||||
|
|
||||||
import '@styles/drilling_program.less'
|
import '@styles/drilling_program.less'
|
||||||
|
|
||||||
const idStateNotInitialized = 0
|
const ID_STATE = {
|
||||||
const idStateApproving = 1
|
NotInitialized: 0,
|
||||||
const idStateCreating = 2
|
Approving: 1,
|
||||||
const idStateReady = 3
|
Creating: 2,
|
||||||
const idStateError = 4
|
Ready: 3,
|
||||||
const idStateUnknown = -1
|
Error: 4,
|
||||||
|
Unknown: -1,
|
||||||
|
}
|
||||||
|
|
||||||
const stateString = {
|
const STATE_STRING = {
|
||||||
[idStateNotInitialized]: { icon: CloseOutlined, text: 'Не настроена' },
|
[ID_STATE.NotInitialized]: { icon: CloseOutlined, text: 'Не настроена' },
|
||||||
[idStateApproving]: { icon: AuditOutlined, text: 'Согласовывается' },
|
[ID_STATE.Approving]: { icon: AuditOutlined, text: 'Согласовывается' },
|
||||||
[idStateCreating]: { icon: LoadingOutlined, text: 'Формируется' },
|
[ID_STATE.Creating]: { icon: LoadingOutlined, text: 'Формируется' },
|
||||||
[idStateReady]: { icon: CheckOutlined, text: 'Сформирована' },
|
[ID_STATE.Ready]: { icon: CheckOutlined, text: 'Сформирована' },
|
||||||
[idStateError]: { icon: WarningOutlined, text: 'Ошибка формирования' },
|
[ID_STATE.Error]: { icon: WarningOutlined, text: 'Ошибка формирования' },
|
||||||
[idStateUnknown]: { icon: WarningOutlined, text: 'Неизвестно' },
|
[ID_STATE.Unknown]: { icon: WarningOutlined, text: 'Неизвестно' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DrillingProgram = memo(() => {
|
export const DrillingProgram = memo(() => {
|
||||||
@ -47,7 +49,7 @@ export const DrillingProgram = memo(() => {
|
|||||||
const [categories, setCategories] = useState([])
|
const [categories, setCategories] = useState([])
|
||||||
const [data, setData] = useState({})
|
const [data, setData] = useState({})
|
||||||
|
|
||||||
const idWell = useContext(IdWellContext)
|
const idWell = useIdWell()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
idState,
|
idState,
|
||||||
@ -57,8 +59,8 @@ export const DrillingProgram = memo(() => {
|
|||||||
error,
|
error,
|
||||||
} = useMemo(() => data, [data])
|
} = useMemo(() => data, [data])
|
||||||
|
|
||||||
const stateId = useMemo(() => idState ?? idStateUnknown, [idState])
|
const stateId = useMemo(() => idState ?? ID_STATE.Unknown, [idState])
|
||||||
const state = useMemo(() => stateString[stateId], [stateId])
|
const state = useMemo(() => STATE_STRING[stateId], [stateId])
|
||||||
const StateIcon = useMemo(() => state.icon, [state?.icon])
|
const StateIcon = useMemo(() => state.icon, [state?.icon])
|
||||||
|
|
||||||
const updateData = useCallback(async () => await invokeWebApiWrapperAsync(
|
const updateData = useCallback(async () => await invokeWebApiWrapperAsync(
|
||||||
@ -89,9 +91,10 @@ export const DrillingProgram = memo(() => {
|
|||||||
setHistoryVisible(!!catId)
|
setHistoryVisible(!!catId)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onEditorClosed = useCallback(() => {
|
const onEditorClosed = useCallback((needUpdate) => {
|
||||||
setEditorVisible(false)
|
setEditorVisible(false)
|
||||||
updateData()
|
if (needUpdate)
|
||||||
|
updateData()
|
||||||
}, [updateData])
|
}, [updateData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -107,7 +110,7 @@ export const DrillingProgram = memo(() => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={'program_content'}>
|
<div className={'program_content'}>
|
||||||
{stateId === idStateReady ? (
|
{stateId === ID_STATE.Ready ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
type={'link'}
|
type={'link'}
|
||||||
@ -120,7 +123,7 @@ export const DrillingProgram = memo(() => {
|
|||||||
<div className={'m-10'}>Размер: {formatBytes(program?.size)}</div>
|
<div className={'m-10'}>Размер: {formatBytes(program?.size)}</div>
|
||||||
<div className={'m-10'}>Сформирован: {formatDate(program?.uploadDate)}</div>
|
<div className={'m-10'}>Сформирован: {formatDate(program?.uploadDate)}</div>
|
||||||
</>
|
</>
|
||||||
) : stateId === idStateError ? (
|
) : stateId === ID_STATE.Error ? (
|
||||||
<>
|
<>
|
||||||
<h3 className={'program_status error'}>
|
<h3 className={'program_status error'}>
|
||||||
<StateIcon className={'m-10'} />
|
<StateIcon className={'m-10'} />
|
||||||
|
@ -168,11 +168,11 @@ export const ReportEditor = memo(({ visible, data, onDone, onCancel, checkIsDate
|
|||||||
if (data) return
|
if (data) return
|
||||||
const newData = await DailyReportService.getOrGenerate(idWell, date.format('YYYY-MM-DD') + 'Z')
|
const newData = await DailyReportService.getOrGenerate(idWell, date.format('YYYY-MM-DD') + 'Z')
|
||||||
if (checkIsDateBusy(moment(newData.reportDate)))
|
if (checkIsDateBusy(moment(newData.reportDate)))
|
||||||
throw 'Рапорт на данную дату уже существует'
|
throw new Error('Рапорт на данную дату уже существует')
|
||||||
setFields(newData)
|
setFields(newData)
|
||||||
},
|
},
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
'Не удалось загрузить автозаполняемые данные для нового рапорта',
|
(e) => `Не удалось загрузить автозаполняемые данные для нового рапорта: ${e}`,
|
||||||
'Получение автозаполняемых данных суточного рапорта',
|
'Получение автозаполняемых данных суточного рапорта',
|
||||||
), [idWell, data, setFields, checkIsDateBusy])
|
), [idWell, data, setFields, checkIsDateBusy])
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ export const DailyReport = memo(() => {
|
|||||||
const checkIsDateBusy = useCallback((current) => current.isAfter(moment(), 'day') || data.some((row) => moment(row.reportDate).isSame(current, 'day')), [data])
|
const checkIsDateBusy = useCallback((current) => current.isAfter(moment(), 'day') || data.some((row) => moment(row.reportDate).isSame(current, 'day')), [data])
|
||||||
|
|
||||||
const columns = useMemo(() => [
|
const columns = useMemo(() => [
|
||||||
makeDateColumn('Дата', 'reportDate', undefined, 'YYYY.MM.DD', { width: 300 }),
|
makeDateColumn('Дата', 'reportDate', undefined, 'DD.MM.YYYY', { width: 300 }),
|
||||||
makeColumn('', '', { width: 200, render: (_, report) => (
|
makeColumn('', '', { width: 200, render: (_, report) => (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
export { isRawDate, formatDate, defaultFormat, periodToString } from './datetime'
|
export { isRawDate, formatDate, defaultFormat, periodToString } from './datetime'
|
||||||
export type { RawDate, timeInS } from './datetime'
|
export type { RawDate, timeInS } from './datetime'
|
||||||
|
|
||||||
|
export { MimeTypes } from './mimeTypes'
|
||||||
|
|
||||||
export const headerHeight: number = 64
|
export const headerHeight: number = 64
|
||||||
|
|
||||||
export const mainFrameSize = () => ({
|
export const mainFrameSize = () => ({
|
||||||
|
34
src/utils/mimeTypes.ts
Normal file
34
src/utils/mimeTypes.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export const MimeTypes = {
|
||||||
|
BMP: 'image/bmp',
|
||||||
|
CSV: 'text/csv',
|
||||||
|
CSV1: 'application/csv',
|
||||||
|
DOC: 'application/msword',
|
||||||
|
DOCM: 'application/vnd.ms-word.document.macroEnabled.12',
|
||||||
|
DOCX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
DOTM: 'application/vnd.ms-word.template.macroEnabled.12',
|
||||||
|
DOTX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||||
|
GIF: 'image/gif',
|
||||||
|
HTML: 'text/html',
|
||||||
|
JPEG: 'image/jpeg',
|
||||||
|
JPG: 'image/jpeg',
|
||||||
|
JSON: 'application/json',
|
||||||
|
PDF: 'application/pdf',
|
||||||
|
PNG: 'image/png',
|
||||||
|
POTM: 'application/vnd.ms-powerpoint.template.macroEnabled.12',
|
||||||
|
POTX: 'application/vnd.openxmlformats-officedocument.presentationml.template',
|
||||||
|
PPAM: 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
|
||||||
|
PPS: 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||||
|
PPSM: 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
|
||||||
|
PPSX: 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||||
|
PPT: 'application/vnd.ms-powerpoint',
|
||||||
|
PPTM: 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||||
|
PPTX: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||||
|
RTF: 'application/rtf',
|
||||||
|
RTF2: 'text/rtf',
|
||||||
|
TXT: 'text/plain',
|
||||||
|
XLSB: 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||||
|
XLSM: 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||||
|
XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MimeTypes
|
Loading…
Reference in New Issue
Block a user