added organ in cms

This commit is contained in:
2024-10-26 20:24:36 +08:00
parent dcf4e3efe8
commit 1475714c7a
8 changed files with 782 additions and 4 deletions

View File

@@ -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

View File

@@ -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"

View 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

View 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