From 3fcbe03299b14acdd088e61fbc8b09a7a3c7c369 Mon Sep 17 00:00:00 2001 From: Dmytro Honchar <90138904+mxrcury@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:24:06 +0300 Subject: [PATCH] Added "Add attachment" to attachments table (#1110) * Inplemented uploading files * Small fix * Added type * Move uploadin to modal window * Small fix * Fixed buttons * Fixed buttons * Fixed comments * Fixed comment * Added sorting to quizzes tab (#1043) * added sorting to quizzes tab * removed redundant destructured variable * renamed variable * fixed code smell * Add sort to attachments on myResources (#1059) * fix conflicts * fix conflicts 3 * init * finished * fix * fix * fix * fix * refactored styles * fixed conflicts * fix --------- Co-authored-by: Yura Didenko Co-authored-by: Taras Chaika <45912519+TSlashDreamy@users.noreply.github.com> Co-authored-by: Artur Bekh <102412173+ArturBekhDEV@users.noreply.github.com> --- .../file-uploader/FileUploader.styles.ts | 13 ---- src/components/file-uploader/FileUploader.tsx | 18 ++--- .../add-attachments/AddAttachments.tsx | 1 + src/containers/add-documents/AddDocuments.tsx | 28 ++++++-- .../my-quizzes/QuizzesContainer.tsx | 1 + .../AddResourceWithInput.tsx | 28 +++++--- .../AttachmentsContainer.styles.ts | 9 ++- .../AttachmentsContainer.tsx | 72 +++++++++++++++---- .../add-photo-step/AddPhotoStep.jsx | 1 + .../add-photo-step/AddPhotoStep.style.js | 15 ++++ .../CreateOrEditLesson.styles.ts | 4 +- src/services/attachment-service.ts | 17 +++++ 12 files changed, 157 insertions(+), 50 deletions(-) create mode 100644 src/services/attachment-service.ts diff --git a/src/components/file-uploader/FileUploader.styles.ts b/src/components/file-uploader/FileUploader.styles.ts index 4d505e605..0e51453b7 100644 --- a/src/components/file-uploader/FileUploader.styles.ts +++ b/src/components/file-uploader/FileUploader.styles.ts @@ -1,16 +1,6 @@ import { TypographyVariantEnum } from '~/types' export const styles = { - root: { - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-around', - border: '1px solid', - borderColor: 'primary.200', - borderRadius: '5px', - maxWidth: '270px', - overflow: 'auto' - }, rootDrag: { borderColor: 'primary.900', backgroundColor: 'basic.grey' @@ -36,9 +26,6 @@ export const styles = { color: 'primary.700', fontSize: '20px' }, - uploadBtn: { - textAlign: 'center' - }, fileSize: { mt: '10px', typography: TypographyVariantEnum.Body2 diff --git a/src/components/file-uploader/FileUploader.tsx b/src/components/file-uploader/FileUploader.tsx index 4955339fb..c7fcc0870 100644 --- a/src/components/file-uploader/FileUploader.tsx +++ b/src/components/file-uploader/FileUploader.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, ReactElement } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' @@ -15,12 +15,12 @@ import useUpload from '~/hooks/use-upload' import { styles } from '~/components/file-uploader/FileUploader.styles' import { AddDocuments, + ButtonVariantEnum, ComponentEnum, Emitter, InputEnum, SizeEnum } from '~/types' -import { spliceSx } from '~/utils/helper-functions' interface FileUploaderProps { buttonText: string @@ -33,6 +33,8 @@ interface FileUploaderProps { root?: SxProps button?: SxProps } + variant?: ButtonVariantEnum + icon?: ReactElement } const FileUploader: FC = ({ @@ -42,7 +44,9 @@ const FileUploader: FC = ({ initialError = '', validationData, isImages = false, - sx = {} + sx = {}, + variant, + icon }) => { const { t } = useTranslation() @@ -66,19 +70,17 @@ const FileUploader: FC = ({ )) const uploadButton = ( - ) return ( <> - + {initialState.length && isImages ? ( {filesList} ) : ( diff --git a/src/containers/add-attachments/AddAttachments.tsx b/src/containers/add-attachments/AddAttachments.tsx index 54c7630b1..ac34e62cf 100644 --- a/src/containers/add-attachments/AddAttachments.tsx +++ b/src/containers/add-attachments/AddAttachments.tsx @@ -129,6 +129,7 @@ const AddAttachments: FC = ({ message: error ? `errors.${error.code}` : '' }) } + const { fetchData: fetchCreateAttachment } = useAxios({ service: createAttachments, fetchOnMount: false, diff --git a/src/containers/add-documents/AddDocuments.tsx b/src/containers/add-documents/AddDocuments.tsx index cbc67366b..909fe88d0 100644 --- a/src/containers/add-documents/AddDocuments.tsx +++ b/src/containers/add-documents/AddDocuments.tsx @@ -1,24 +1,35 @@ -import { FC, useEffect, useState } from 'react' +import { FC, ReactElement, useEffect, useState } from 'react' import Box from '@mui/material/Box' +import { SxProps } from '@mui/material' import FileUploader from '~/components/file-uploader/FileUploader' -import { validationData } from '~/containers/add-documents/AddDocuments.constants' import { useSnackBarContext } from '~/context/snackbar-context' -import { snackbarVariants } from '~/constants' +import { validationData } from '~/containers/add-documents/AddDocuments.constants' import { styles } from '~/containers/add-documents/AddDocuments.styles' -import { Emitter } from '~/types' +import { snackbarVariants } from '~/constants' +import { ButtonVariantEnum, Emitter } from '~/types' +import { spliceSx } from '~/utils/helper-functions' interface AddDocumentsProps { fetchData: (formData: FormData) => Promise formData: FormData buttonText: string + variant?: ButtonVariantEnum + sx?: { + root?: SxProps + button?: SxProps + } + icon?: ReactElement } const AddDocuments: FC = ({ fetchData, formData, - buttonText + buttonText, + variant, + sx = {}, + icon }) => { const [documents, setDocuments] = useState([]) const [documentsError, setDocumentsError] = useState('') @@ -50,10 +61,15 @@ const AddDocuments: FC = ({ ) diff --git a/src/containers/my-quizzes/QuizzesContainer.tsx b/src/containers/my-quizzes/QuizzesContainer.tsx index ab517b55e..9ede05863 100644 --- a/src/containers/my-quizzes/QuizzesContainer.tsx +++ b/src/containers/my-quizzes/QuizzesContainer.tsx @@ -18,6 +18,7 @@ import { itemsLoadLimit, removeColumnRules } from '~/containers/my-quizzes/QuizzesContainer.constants' + import { ItemsWithCount, GetResourcesParams, diff --git a/src/containers/my-resources/add-resource-with-input/AddResourceWithInput.tsx b/src/containers/my-resources/add-resource-with-input/AddResourceWithInput.tsx index c15c99097..8b04587e3 100644 --- a/src/containers/my-resources/add-resource-with-input/AddResourceWithInput.tsx +++ b/src/containers/my-resources/add-resource-with-input/AddResourceWithInput.tsx @@ -1,4 +1,10 @@ -import { useState, ChangeEvent, FC, MutableRefObject } from 'react' +import { + useState, + ChangeEvent, + FC, + MutableRefObject, + ReactElement +} from 'react' import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import SearchIcon from '@mui/icons-material/Search' @@ -12,17 +18,19 @@ import InputWithIcon from '~/components/input-with-icon/InputWithIcon' import { styles } from '~/containers/my-resources/add-resource-with-input/AddResourceWithInput.styles' interface AddResourceWithInputProps { - btnText: string + btnText?: string fetchData: () => Promise - link: string + link?: string searchRef: MutableRefObject + button?: ReactElement } const AddResourceWithInput: FC = ({ btnText, fetchData, link, - searchRef + searchRef, + button }) => { const { t } = useTranslation() const [searchInput, setSearchInput] = useState('') @@ -45,10 +53,14 @@ const AddResourceWithInput: FC = ({ return ( - - {t(btnText)} - - + {!button ? ( + + {!button && btnText && t(btnText)} + + + ) : ( + button + )} } diff --git a/src/containers/my-resources/attachments-container/AttachmentsContainer.styles.ts b/src/containers/my-resources/attachments-container/AttachmentsContainer.styles.ts index 66a59dbe8..8b83362bb 100644 --- a/src/containers/my-resources/attachments-container/AttachmentsContainer.styles.ts +++ b/src/containers/my-resources/attachments-container/AttachmentsContainer.styles.ts @@ -21,7 +21,14 @@ export const styles = { searchIcon: { color: 'primary.700' }, - addAttachmentBtn: { width: 'fit-content' }, + addAttachmentBtn: { + button: { + backgroundColor: 'primary.900', + typography: TypographyVariantEnum.Body1, + fontWeight: '500', + py: '12.5px' + } + }, addAttachmentIcon: { ml: '5px', width: { xs: '18px', sm: '22px' } }, table: { '& td,th': { diff --git a/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx b/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx index c98045a35..326531eed 100644 --- a/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx +++ b/src/containers/my-resources/attachments-container/AttachmentsContainer.tsx @@ -1,5 +1,7 @@ import { useCallback, useRef, useState } from 'react' import Box from '@mui/material/Box' +import AddIcon from '@mui/icons-material/Add' +import { useTranslation } from 'react-i18next' import { useSnackBarContext } from '~/context/snackbar-context' import { ResourceService } from '~/services/resource-service' @@ -10,6 +12,8 @@ import useSort from '~/hooks/table/use-sort' import useBreakpoints from '~/hooks/use-breakpoints' import useAxios from '~/hooks/use-axios' import usePagination from '~/hooks/table/use-pagination' +import AddDocuments from '~/containers/add-documents/AddDocuments' +import { attachmentService } from '~/services/attachment-service' import { defaultResponses, snackbarVariants } from '~/constants' import { @@ -24,18 +28,21 @@ import { Attachment, ErrorResponse, UpdateAttachmentParams, - ResourcesTabsEnum + ResourcesTabsEnum, + ButtonVariantEnum } from '~/types' import { ajustColumns, getScreenBasedLimit } from '~/utils/helper-functions' import { styles } from '~/containers/my-resources/attachments-container/AttachmentsContainer.styles' const AttachmentsContainer = () => { + const { t } = useTranslation() const { setAlert } = useSnackBarContext() const breakpoints = useBreakpoints() const { page } = usePagination() const sortOptions = useSort({ initialSort }) const searchFileName = useRef('') const [selectedItemId, setSelectedItemId] = useState('') + const formData = new FormData() const { sort } = sortOptions const itemsPerPage = getScreenBasedLimit(breakpoints, itemsLoadLimit) @@ -76,16 +83,20 @@ const AttachmentsContainer = () => { [] ) - const { response, loading, fetchData } = useAxios< - ItemsWithCount, - GetResourcesParams - >({ + const { + response, + loading, + fetchData: fetchAttachments + } = useAxios, GetResourcesParams>({ service: getAttachments, defaultResponse: defaultResponses.itemsWithCount, onResponseError }) - const onAttachmentUpdate = useCallback(() => void fetchData(), [fetchData]) + const onAttachmentUpdate = useCallback( + () => void fetchAttachments(), + [fetchAttachments] + ) const { fetchData: updateData } = useAxios({ service: updateAttachment, @@ -95,6 +106,29 @@ const AttachmentsContainer = () => { fetchOnMount: false }) + const createAttachments = useCallback( + (data?: FormData) => attachmentService.createAttachments(data), + [] + ) + + const onCreateAttachmentsError = (error: ErrorResponse) => { + setAlert({ + severity: snackbarVariants.error, + message: error ? `errors.${error.code}` : '' + }) + } + const { fetchData: fetchCreateAttachment } = useAxios({ + service: createAttachments, + fetchOnMount: false, + defaultResponse: null, + onResponseError: onCreateAttachmentsError + }) + + const uploadFile = async (data: FormData) => { + await fetchCreateAttachment(data) + await fetchAttachments() + } + const onSave = async (fileName: string) => { const id = selectedItemId setSelectedItemId('') @@ -111,7 +145,7 @@ const AttachmentsContainer = () => { const props = { columns: columnsToShow, - data: { response, getData: fetchData }, + data: { response, getData: fetchAttachments }, services: { deleteService: deleteAttachment }, itemsPerPage, actions: { onEdit }, @@ -120,14 +154,26 @@ const AttachmentsContainer = () => { sx: styles.table } + const addAttachmentBlock = ( + } + sx={styles.addAttachmentBtn} + variant={ButtonVariantEnum.Contained} + /> + } + fetchData={fetchAttachments} + searchRef={searchFileName} + /> + ) + return ( - + {addAttachmentBlock} {loading ? ( ) : ( diff --git a/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.jsx b/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.jsx index ee410c7a4..3a3c95415 100644 --- a/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.jsx +++ b/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.jsx @@ -81,6 +81,7 @@ const AddPhotoStep = ({ btnsBox, stepLabel }) => { initialError={photoError} initialState={photo} isImages + sx={style.fileUploader} validationData={validationData} /> diff --git a/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.style.js b/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.style.js index 4f006642f..f3df4e611 100644 --- a/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.style.js +++ b/src/containers/tutor-home-page/add-photo-step/AddPhotoStep.style.js @@ -49,5 +49,20 @@ export const style = { }, description: { mb: '20px' + }, + fileUploader: { + button: { + textAlign: 'center' + }, + root: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-around', + border: '1px solid', + borderColor: 'primary.200', + borderRadius: '5px', + maxWidth: '270px', + overflow: 'auto' + } } } diff --git a/src/pages/create-or-edit-lesson/CreateOrEditLesson.styles.ts b/src/pages/create-or-edit-lesson/CreateOrEditLesson.styles.ts index 6990d5dd0..113c992f7 100644 --- a/src/pages/create-or-edit-lesson/CreateOrEditLesson.styles.ts +++ b/src/pages/create-or-edit-lesson/CreateOrEditLesson.styles.ts @@ -61,5 +61,7 @@ export const styles = { fontSize: '25px', marginLeft: '13px', fontWeight: '400' - } + }, + addAttachmentBtn: { width: 'fit-content' }, + addAttachmentIcon: { ml: '5px', width: { xs: '18px', sm: '22px' } } } diff --git a/src/services/attachment-service.ts b/src/services/attachment-service.ts new file mode 100644 index 000000000..a4f086eee --- /dev/null +++ b/src/services/attachment-service.ts @@ -0,0 +1,17 @@ +import { AxiosResponse } from 'axios' +import { URLs } from '~/constants/request' +import { axiosClient } from '~/plugins/axiosClient' +import { GetAttachmentsParams } from '~/types' + +export const attachmentService = { + getAttachments: async ( + params: GetAttachmentsParams + ): Promise => + await axiosClient.get(URLs.resources.attachments.get, { params }), + + createAttachments: (data?: FormData): Promise => { + return axiosClient.post(URLs.attachments.post, data, { + headers: { 'Content-Type': 'multipart/form-data' } + }) + } +}