added organ in cms
This commit is contained in:
		| @@ -9,12 +9,13 @@ import { | ||||
| import { BsThreeDotsVertical } from "react-icons/bs" | ||||
| import { FiEdit, FiTrash } from "react-icons/fi" | ||||
|  | ||||
| import type { ItemPublic, UserPublic, AboutUsPublic, CoursePublic, ImagePublic, SchedulePublic } from "../../client" | ||||
| import type { ItemPublic, UserPublic, AboutUsPublic, CoursePublic, ImagePublic, SchedulePublic, OrganPublic } from "../../client" | ||||
| import EditUser from "../Admin/EditUser" | ||||
| import EditItem from "../Items/EditItem" | ||||
| import EditCourseImage from "../CourseImage/editCourseImage" | ||||
| import EditAboutUs from "../AboutUs/EditAboutUs" | ||||
| import EditSechedule from "../Courses/EditSechedule" | ||||
| import EditOrgan from "../Organ/EditOrgan" | ||||
| import Delete from "./DeleteAlert" | ||||
|  | ||||
| interface ActionsMenuProps { | ||||
| @@ -58,6 +59,14 @@ const ActionsMenu = ({ type, value, disabled }: ActionsMenuProps) => { | ||||
|             onClose={editUserModal.onClose} | ||||
|           /> | ||||
|         ) | ||||
|       case 'Organ': | ||||
|         return ( | ||||
|           <EditOrgan | ||||
|             organ={value as OrganPublic} | ||||
|             isOpen={editUserModal.isOpen} | ||||
|             onClose={editUserModal.onClose} | ||||
|           /> | ||||
|         ) | ||||
|       case 'Image': | ||||
|         return ( | ||||
|           <EditCourseImage | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query" | ||||
| import React from "react" | ||||
| import { useForm } from "react-hook-form" | ||||
|  | ||||
| import { ItemsService, UsersService, ClientMessagesService, AboutUsService, CoursesService, ImageService, Info_imageService,secheduleService } from "../../client" | ||||
| import { ItemsService, UsersService, ClientMessagesService, AboutUsService, CoursesService, ImageService, Info_imageService, secheduleService, OrganService } from "../../client" | ||||
| import useCustomToast from "../../hooks/useCustomToast" | ||||
|  | ||||
| interface DeleteProps { | ||||
| @@ -39,7 +39,10 @@ const Delete = ({ type, id, isOpen, onClose }: DeleteProps) => { | ||||
|       await ClientMessagesService.deleteMessage({ id: id }) | ||||
|     } else if (type === "AboutUs") { | ||||
|       await AboutUsService.deleteAboutUs({ id: id }) | ||||
|     } else if (type === "Course") { | ||||
|     } else if (type === "Organ") { | ||||
|       await OrganService.deleteOrgan({ id: id }) | ||||
|     } | ||||
|     else if (type === "Course") { | ||||
|       await CoursesService.deleteCourse({ id: id }) | ||||
|     } else if (type === "Image") { | ||||
|       await ImageService.deleteImage({ id: id }) | ||||
| @@ -82,11 +85,13 @@ const Delete = ({ type, id, isOpen, onClose }: DeleteProps) => { | ||||
|         key = "messages" | ||||
|       } else if (type === "AboutUs") { | ||||
|         key = "aboutUs" | ||||
|       } else if(type === "Organ"){ | ||||
|         key = "organs" | ||||
|       } else if (type === "Course") { | ||||
|         key = "courses" | ||||
|       } else if (type === "Image") { | ||||
|         key = "course" | ||||
|       } else if(type === "Info_Image"){ | ||||
|       } else if (type === "Info_Image") { | ||||
|         key = "course" | ||||
|       } else if (type === "Sechedule") { | ||||
|         key = "course" | ||||
|   | ||||
							
								
								
									
										226
									
								
								frontend/src/components/Organ/AddOrgan.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								frontend/src/components/Organ/AddOrgan.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,226 @@ | ||||
| import { useState } from 'react'; | ||||
| import { | ||||
|   Button, | ||||
|   FormControl, | ||||
|   FormErrorMessage, | ||||
|   FormLabel, | ||||
|   Modal, | ||||
|   ModalBody, | ||||
|   ModalCloseButton, | ||||
|   ModalContent, | ||||
|   ModalFooter, | ||||
|   ModalHeader, | ||||
|   ModalOverlay, | ||||
|   NumberInput, | ||||
|   NumberInputField, | ||||
|   NumberInputStepper, | ||||
|   NumberIncrementStepper, | ||||
|   NumberDecrementStepper, | ||||
|   Input, | ||||
| } from "@chakra-ui/react" | ||||
|  | ||||
| import { useMutation, useQueryClient } from "@tanstack/react-query" | ||||
| import { type SubmitHandler, useForm } from "react-hook-form" | ||||
| import { type ApiError, type OrganCreate, OrganService } from "../../client" | ||||
| import useCustomToast from "../../hooks/useCustomToast" | ||||
| import { handleError } from "../../utils" | ||||
| import { EditorState, convertToRaw } from 'draft-js'; | ||||
| import { Editor } from "react-draft-wysiwyg"; | ||||
| import draftToHtml from 'draftjs-to-html'; | ||||
| import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; | ||||
|  | ||||
| interface AddOrganProps { | ||||
|   isOpen: boolean | ||||
|   onClose: () => void | ||||
| } | ||||
|  | ||||
| // type FileUploadProps = { | ||||
| //   register: UseFormRegisterReturn | ||||
| //   accept?: string | ||||
| //   multiple?: boolean | ||||
| //   children?: ReactNode | ||||
| // } | ||||
|  | ||||
| // const FileUpload = (props: FileUploadProps) => { | ||||
| //   const { register, accept, multiple, children } = props | ||||
| //   const inputRef = useRef<HTMLInputElement | null>(null) | ||||
| //   const { ref, ...rest } = register as { ref: (instance: HTMLInputElement | null) => void } | ||||
|  | ||||
| //   const handleClick = () => inputRef.current?.click() | ||||
|  | ||||
| //   return ( | ||||
| //     <InputGroup onClick={handleClick}> | ||||
| //       <input | ||||
| //         type={'file'} | ||||
| //         multiple={multiple || false} | ||||
| //         hidden | ||||
| //         accept={accept} | ||||
| //         {...rest} | ||||
| //         ref={(e) => { | ||||
| //           ref(e) | ||||
| //           inputRef.current = e | ||||
| //         }} | ||||
| //       /> | ||||
| //       <> | ||||
| //         {children} | ||||
| //       </> | ||||
| //     </InputGroup> | ||||
| //   ) | ||||
| // } | ||||
|  | ||||
|  | ||||
|  | ||||
| const AddOrgan = ({ isOpen, onClose }: AddOrganProps) => { | ||||
|   const queryClient = useQueryClient() | ||||
|   const showToast = useCustomToast() | ||||
|   const { | ||||
|     register, | ||||
|     handleSubmit, | ||||
|     reset, | ||||
|     formState: { errors, isSubmitting }, | ||||
|   } = useForm<OrganCreate>({ | ||||
|     mode: "onBlur", | ||||
|     criteriaMode: "all", | ||||
|     defaultValues: { | ||||
|       index: 0, | ||||
|       title: "", | ||||
|       description: "", | ||||
|       image: undefined, | ||||
|     }, | ||||
|   }) | ||||
|   const [editorState, setEditorState] = useState<EditorState>(EditorState.createEmpty()); | ||||
|   const [content, setContent] = useState<string>(''); | ||||
|  | ||||
|  | ||||
|  | ||||
|   const mutation = useMutation({ | ||||
|     mutationFn: (data: OrganCreate) => | ||||
|       OrganService.createOrgan({ formData: data }), | ||||
|     onSuccess: () => { | ||||
|       showToast("Success!", "Organ created successfully.", "success") | ||||
|       reset() | ||||
|       setEditorState(EditorState.createEmpty()); | ||||
|       onClose() | ||||
|     }, | ||||
|     onError: (err: ApiError) => { | ||||
|       handleError(err, showToast) | ||||
|     }, | ||||
|     onSettled: () => { | ||||
|       queryClient.invalidateQueries({ queryKey: ["organ"] }) | ||||
|     }, | ||||
|   }) | ||||
|  | ||||
|   const onSubmit: SubmitHandler<OrganCreate> = (data) => { | ||||
|  | ||||
|  | ||||
|     if (isSubmitting) return | ||||
|  | ||||
|     try { | ||||
|  | ||||
|       if (data.image instanceof FileList && data.image.length > 0) { | ||||
|         data.image = data.image[0] | ||||
|       } | ||||
|       mutation.mutate(data) | ||||
|       console.log(data) | ||||
|  | ||||
|     } | ||||
|     catch (error) { | ||||
|       console.error("Error:", error); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|   return ( | ||||
|     <> | ||||
|       <Modal | ||||
|         isOpen={isOpen} | ||||
|         onClose={onClose} | ||||
|         size={'xl'} | ||||
|         isCentered | ||||
|       > | ||||
|         <ModalOverlay /> | ||||
|         <ModalContent as="form" onSubmit={handleSubmit(onSubmit)}> | ||||
|           <ModalHeader>Add Organ</ModalHeader> | ||||
|           <ModalCloseButton /> | ||||
|  | ||||
|           <ModalBody pb={30}> | ||||
|             <FormControl isRequired isInvalid={!!errors.title}> | ||||
|               <FormLabel htmlFor="title">Title</FormLabel> | ||||
|               <Input | ||||
|                 id="title" | ||||
|                 {...register("title", { | ||||
|                   required: "Title is required.", | ||||
|                 })} | ||||
|                 placeholder="Title" | ||||
|                 type="text" | ||||
|               /> | ||||
|               {errors.title && ( | ||||
|                 <FormErrorMessage>{errors.title.message}</FormErrorMessage> | ||||
|               )} | ||||
|             </FormControl> | ||||
|             <FormControl isRequired isInvalid={!!errors.description}> | ||||
|               <Editor | ||||
|                 editorState={editorState} | ||||
|                 wrapperClassName="wrapper-class" | ||||
|                 editorClassName="demo-editor" | ||||
|                 onEditorStateChange={newState => { | ||||
|                   setEditorState(newState); | ||||
|                   setContent(draftToHtml(convertToRaw(newState.getCurrentContent()))); | ||||
|                   reset({ | ||||
|                     description: content, | ||||
|                   }); | ||||
|  | ||||
|                 }} | ||||
|                 toolbar={{ | ||||
|                   options: ['inline', 'blockType', 'fontSize', 'list', 'textAlign', 'history', 'embedded', 'emoji', 'image'], | ||||
|                   inline: { inDropdown: true }, | ||||
|                   list: { inDropdown: true }, | ||||
|                   textAlign: { inDropdown: true }, | ||||
|                   link: { inDropdown: true }, | ||||
|                   history: { inDropdown: true }, | ||||
|                 }} | ||||
|               /> | ||||
|             </FormControl> | ||||
|             <FormControl isRequired isInvalid={!!errors.index}> | ||||
|               <FormLabel htmlFor="index">Index</FormLabel > | ||||
|               <NumberInput min={0} max={20}  > | ||||
|                 <NumberInputField {...register("index", { | ||||
|                   required: "index is required.", | ||||
|                 })} /> | ||||
|                 <NumberInputStepper> | ||||
|                   <NumberIncrementStepper /> | ||||
|                   <NumberDecrementStepper /> | ||||
|                 </NumberInputStepper> | ||||
|               </NumberInput> | ||||
|  | ||||
|               {errors.index && ( | ||||
|                 <FormErrorMessage>{errors.index.message}</FormErrorMessage> | ||||
|               )} | ||||
|             </FormControl> | ||||
|  | ||||
|             <FormControl isInvalid={!!errors.image} isRequired> | ||||
|               <FormLabel>{'Image Upload'}</FormLabel> | ||||
|  | ||||
|  | ||||
|               <input type="file" {...register("image", { | ||||
|                 required: "index is required.", | ||||
|               })} /> | ||||
|               <FormErrorMessage> | ||||
|                 {errors.image && errors?.image.message} | ||||
|               </FormErrorMessage> | ||||
|             </FormControl> | ||||
|  | ||||
|           </ModalBody> | ||||
|  | ||||
|           <ModalFooter gap={3}> | ||||
|             <Button variant="primary" type="submit" isLoading={isSubmitting || mutation.isPending}> | ||||
|               Save | ||||
|             </Button> | ||||
|             <Button onClick={onClose}>Cancel</Button> | ||||
|           </ModalFooter> | ||||
|         </ModalContent> | ||||
|       </Modal> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default AddOrgan | ||||
							
								
								
									
										246
									
								
								frontend/src/components/Organ/EditOrgan.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								frontend/src/components/Organ/EditOrgan.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| import {useState } from 'react'; | ||||
| import { | ||||
|     Button, | ||||
|     FormControl, | ||||
|     FormErrorMessage, | ||||
|     FormLabel, | ||||
|     Input, | ||||
|     Modal, | ||||
|     ModalBody, | ||||
|     ModalCloseButton, | ||||
|     ModalContent, | ||||
|     ModalFooter, | ||||
|     ModalHeader, | ||||
|     ModalOverlay, | ||||
|     NumberInput, | ||||
|     NumberInputField, | ||||
|     NumberInputStepper, | ||||
|     NumberIncrementStepper, | ||||
|     NumberDecrementStepper, | ||||
|     Box, | ||||
|     Image, | ||||
| } from "@chakra-ui/react" | ||||
|  | ||||
| import { useMutation, useQueryClient } from "@tanstack/react-query" | ||||
| import { type SubmitHandler, useForm } from "react-hook-form" | ||||
| import { type ApiError,  OrganService, OrganUpdate, OrganPublic } from "../../client" | ||||
| import useCustomToast from "../../hooks/useCustomToast" | ||||
| import { handleError } from "../../utils" | ||||
| import { EditorState, ContentState, convertToRaw } from 'draft-js'; | ||||
| import { Editor } from "react-draft-wysiwyg"; | ||||
| import  draftToHtml  from 'draftjs-to-html'; | ||||
| import htmlToDraft from 'html-to-draftjs'; | ||||
| import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; | ||||
|  | ||||
| interface EditOrganProps { | ||||
|     isOpen: boolean | ||||
|     onClose: () => void | ||||
|     organ: OrganPublic | ||||
| } | ||||
|  | ||||
| // type FileUploadProps = { | ||||
| //     register: UseFormRegisterReturn | ||||
| //     accept?: string | ||||
| //     multiple?: boolean | ||||
| //     children?: ReactNode | ||||
| // } | ||||
|  | ||||
| // const FileUpload = (props: FileUploadProps) => { | ||||
| //     const { register, accept, multiple, children } = props | ||||
| //     const inputRef = useRef<HTMLInputElement | null>(null) | ||||
| //     const { ref, ...rest } = register as { ref: (instance: HTMLInputElement | null) => void } | ||||
|  | ||||
| //     const handleClick = () => inputRef.current?.click() | ||||
|  | ||||
| //     return ( | ||||
| //         <InputGroup onClick={handleClick}> | ||||
| //             <input | ||||
| //                 type={'file'} | ||||
| //                 multiple={multiple || false} | ||||
| //                 hidden | ||||
| //                 accept={accept} | ||||
| //                 {...rest} | ||||
| //                 ref={(e) => { | ||||
| //                     ref(e) | ||||
| //                     inputRef.current = e | ||||
| //                 }} | ||||
| //             /> | ||||
| //             <> | ||||
| //                 {children} | ||||
| //             </> | ||||
| //         </InputGroup> | ||||
| //     ) | ||||
| // } | ||||
|  | ||||
|  | ||||
|  | ||||
| const EditOrgan = ({ organ, isOpen, onClose }: EditOrganProps) => { | ||||
|     //const url = import.meta.env.VITE_API_URL | ||||
|     const queryClient = useQueryClient() | ||||
|     const showToast = useCustomToast() | ||||
|     const { | ||||
|         register, | ||||
|         handleSubmit, | ||||
|         reset, | ||||
|         formState: { errors, isSubmitting }, | ||||
|     } = useForm<OrganUpdate>({ | ||||
|         mode: "onBlur", | ||||
|         criteriaMode: "all", | ||||
|         defaultValues: { | ||||
|             index: organ.index, | ||||
|             title: organ.title, | ||||
|             description: organ.description, | ||||
|             image: undefined, | ||||
|         }, | ||||
|     }) | ||||
|     const [editorState, setEditorState] = useState<EditorState>(() => { | ||||
|         const contentBlock = htmlToDraft(organ.description); | ||||
|         if (contentBlock) { | ||||
|             const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks); | ||||
|             return EditorState.createWithContent(contentState); | ||||
|         } | ||||
|         return EditorState.createEmpty(); | ||||
|     }); | ||||
|    // const [content, setContent] = useState<string>(aboutUs.description); | ||||
|  | ||||
|     // const validateFiles = (value: File) => { | ||||
|     //     if (typeof value === 'string') return true; | ||||
|  | ||||
|  | ||||
|     //     const fsMb = value.size / (1024 * 1024) | ||||
|     //     const MAX_FILE_SIZE = 10 | ||||
|     //     if (fsMb > MAX_FILE_SIZE) { | ||||
|     //         return 'Max file size 10mb' | ||||
|     //     } | ||||
|  | ||||
|     //     return true | ||||
|     // } | ||||
|  | ||||
|  | ||||
|     // type FormValues = { | ||||
|     //     file_: FileList | ||||
|     // } | ||||
|  | ||||
|  | ||||
|     const mutation = useMutation({ | ||||
|         mutationFn: (data: OrganUpdate) => | ||||
|             OrganService.updateOrgan({ id: organ.id, formData: data }), | ||||
|         onSuccess: () => { | ||||
|             showToast("Success!", "Organ update successfully.", "success") | ||||
|             reset() | ||||
|             setEditorState(EditorState.createEmpty()); | ||||
|             onClose() | ||||
|         }, | ||||
|         onError: (err: ApiError) => { | ||||
|             handleError(err, showToast) | ||||
|         }, | ||||
|         onSettled: () => { | ||||
|             queryClient.invalidateQueries({ queryKey: ["organ"] }) | ||||
|         }, | ||||
|     }) | ||||
|  | ||||
|     const onSubmit: SubmitHandler<OrganUpdate> = (data) => { | ||||
|         if (data.image instanceof FileList && data.image.length > 0) { | ||||
|             data.image = data.image[0] | ||||
|         }else{ | ||||
|             data.image = null | ||||
|         } | ||||
|         mutation.mutate(data) | ||||
|         console.log(data) | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return ( | ||||
|         <> | ||||
|             <Modal | ||||
|                 isOpen={isOpen} | ||||
|                 onClose={onClose} | ||||
|                 size={'xl'} | ||||
|                 isCentered | ||||
|             > | ||||
|                 <ModalOverlay /> | ||||
|                 <ModalContent as="form" onSubmit={handleSubmit(onSubmit)}> | ||||
|                     <ModalHeader>Edit Organ</ModalHeader> | ||||
|                     <ModalCloseButton /> | ||||
|                     <ModalBody pb={30}> | ||||
|                         <Box boxSize='auto'> | ||||
|                             <Image src={import.meta.env.VITE_IMAGE_URL+"/"+organ.image}  /> | ||||
|                         </Box> | ||||
|                         <FormControl isRequired isInvalid={!!errors.title}> | ||||
|                             <FormLabel htmlFor="title">Title</FormLabel> | ||||
|                             <Input | ||||
|                                 id="title" | ||||
|                                 {...register("title", { | ||||
|                                     required: "Title is required.", | ||||
|                                 })} | ||||
|                                 placeholder="Title" | ||||
|                                 type="text" | ||||
|                             /> | ||||
|                             {errors.title && ( | ||||
|                                 <FormErrorMessage>{errors.title.message}</FormErrorMessage> | ||||
|                             )} | ||||
|                         </FormControl> | ||||
|                         <FormControl isRequired isInvalid={!!errors.description}> | ||||
|                             <Editor | ||||
|                                 editorState={editorState} | ||||
|                                 wrapperClassName="wrapper-class" | ||||
|                                 editorClassName="demo-editor" | ||||
|                                 onEditorStateChange={newState => { | ||||
|                                     setEditorState(newState); | ||||
|                                     const newContent = draftToHtml(convertToRaw(newState.getCurrentContent())); | ||||
|                                     //setContent(newContent); | ||||
|                                     reset({ | ||||
|                                         description: newContent, | ||||
|                                     }); | ||||
|  | ||||
|                                 }} | ||||
|                                 toolbar={{ | ||||
|                                     options: ['inline', 'blockType', 'fontSize', 'list', 'textAlign', 'history', 'embedded', 'emoji', 'image'], | ||||
|                                     inline: { inDropdown: true }, | ||||
|                                     list: { inDropdown: true }, | ||||
|                                     textAlign: { inDropdown: true }, | ||||
|                                     link: { inDropdown: true }, | ||||
|                                     history: { inDropdown: true }, | ||||
|                                 }} | ||||
|                             /> | ||||
|                         </FormControl> | ||||
|                         <FormControl isRequired isInvalid={!!errors.index}> | ||||
|                             <FormLabel htmlFor="index">Index</FormLabel > | ||||
|                             <NumberInput min={0} max={20}  > | ||||
|                                 <NumberInputField {...register("index", { | ||||
|                                     required: "index is required.", | ||||
|                                 })} /> | ||||
|                                 <NumberInputStepper> | ||||
|                                     <NumberIncrementStepper /> | ||||
|                                     <NumberDecrementStepper /> | ||||
|                                 </NumberInputStepper> | ||||
|                             </NumberInput> | ||||
|                             {errors.index && ( | ||||
|                                 <FormErrorMessage>{errors.index.message}</FormErrorMessage> | ||||
|                             )} | ||||
|                         </FormControl> | ||||
|  | ||||
|                         <FormControl isInvalid={!!errors.image} isRequired> | ||||
|                             <FormLabel>{'Image Upload'}</FormLabel> | ||||
|  | ||||
|                             <input type="file" {...register("image")} /> | ||||
|                             <FormErrorMessage> | ||||
|                                 {errors.image && errors?.image.message} | ||||
|                             </FormErrorMessage> | ||||
|                         </FormControl> | ||||
|  | ||||
|                     </ModalBody> | ||||
|  | ||||
|                     <ModalFooter gap={3}> | ||||
|                         <Button variant="primary" type="submit" isLoading={isSubmitting}> | ||||
|                             Save | ||||
|                         </Button> | ||||
|                         <Button onClick={onClose}>Cancel</Button> | ||||
|                     </ModalFooter> | ||||
|                 </ModalContent> | ||||
|             </Modal> | ||||
|         </> | ||||
|     ) | ||||
| } | ||||
| export default EditOrgan | ||||
		Reference in New Issue
	
	Block a user