feat(MoveShelfModal): Add new shelf

This commit is contained in:
0ry5 2025-01-17 15:08:18 +01:00
parent 219b65e95f
commit b25c54915f
9 changed files with 162 additions and 46 deletions
frontend/src/shared/components/modals

View file

@ -1,13 +1,14 @@
import React, { useCallback, useEffect, useState } from "react";
import { Button, Col, Form, Modal, Row } from "react-bootstrap";
import { ImBook, ImCamera } from "react-icons/im";
import { ImBook, ImCamera, ImCancelCircle } from "react-icons/im";
import { ModalHeader } from "./ModalHeader";
import { useForm, useWatch } from "react-hook-form";
import { Book, bookShelfs } from "../../../types/Book";
import { Book } from "../../../types/Book";
import { useScanner } from "../../utils/useScanner";
import { BookModalProps } from "./types";
import { tryGoogleBooksApi } from "../../../pages/Main/utils/tryGoogleBooksApi";
import { tryDeutscheNationalBibliothekApi } from "../../../pages/Main/utils/tryDeutscheNationalBibliothekApi";
import { useQuery } from "@tanstack/react-query";
type BookForm = Pick<Book, "isbn" | "title" | "shelf" | "published">;
@ -19,6 +20,19 @@ export const BookModal = ({
const isEdit = !!book;
const [showScanner, setShowScanner] = useState(false);
const [addNew, setAddNew] = useState<boolean>(false);
const { data: shelves, isLoading } = useQuery<string[]>({
queryFn: async () => {
const response = await fetch(`/api/shelves/list`);
const data = await response.text();
return data
.split(",")
.map((s) => s.replaceAll('"', "").replace("[", "").replace("]", ""));
},
queryKey: ["shleves"],
});
const { control, register, formState, setError, setValue, reset } =
useForm<BookForm>({
mode: "onChange",
@ -84,7 +98,7 @@ export const BookModal = ({
}
if (error) return;
const createdBook = await fetch(
isEdit ? "api/books/edit" : "/api/books/create",
isEdit ? "/api/books/edit" : "/api/books/create",
{
method: "POST",
body: JSON.stringify(data),
@ -213,22 +227,52 @@ export const BookModal = ({
<Form.Label>Shelf</Form.Label>
</Col>
<Col>
<Form.Select
{...register("shelf", { required: true })}
isInvalid={!!formState.errors.shelf}
>
{Object.keys(bookShelfs).map((key) => (
<option
key='key'
value={bookShelfs[key as keyof typeof bookShelfs]}
{!isLoading &&
(addNew ? (
<div className='d-flex'>
<Form.Control
id='move-shelf-text-input'
{...register("shelf", { required: true })}
style={{
borderRadius: "5px 0px 0px 5px",
}}
/>
<Button
style={{
borderRadius: "0px 5px 5px 0px",
}}
className='d-flex mr-2 pt-2'
onClick={() => setAddNew(false)}
>
<div className='m-auto'>
<ImCancelCircle size={20} />
</div>
</Button>
</div>
) : (
<Form.Select
id='move-shelf-select'
{...register("shelf", {
required: true,
onChange: (ev) => {
if (ev.target.value === "addNew") {
setAddNew(true);
}
},
})}
isInvalid={!!formState.errors.shelf}
>
{key}
</option>
{shelves?.map((shelf) => (
<option key='key' value={shelf}>
{shelf}
</option>
))}
<option value='addNew'>Add new</option>
</Form.Select>
))}
</Form.Select>
<Form.Control.Feedback type='invalid'>
{!values.shelf
? "Shelf is required"
? "Target shelf is required"
: formState.errors.shelf?.message}
</Form.Control.Feedback>
</Col>

View file

@ -1,10 +1,10 @@
import { useMutation } from "@tanstack/react-query";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Button, Col, Form, Modal, Row } from "react-bootstrap";
import { ImBook } from "react-icons/im";
import { ImBook, ImCancelCircle } from "react-icons/im";
import { ModalHeader } from "./ModalHeader";
import { MoveShelfAction, MoveShelfModalProps } from "./types";
import { useForm, useWatch } from "react-hook-form";
import { bookShelfs } from "../../../types/Book";
import { useState } from "react";
export const MoveShelfModal = ({
books,
@ -20,7 +20,7 @@ export const MoveShelfModal = ({
const { mutate: mv } = useMutation({
mutationFn: async () => {
if (!values.target) {
setError("target", { message: "Taget shelf is required" });
setError("target", { message: "Target shelf is required" });
return;
}
@ -35,6 +35,19 @@ export const MoveShelfModal = ({
},
});
const { data: shelves, isLoading } = useQuery<string[]>({
queryFn: async () => {
const response = await fetch(`/api/shelves/list`);
const data = await response.text();
return data
.split(",")
.map((s) => s.replaceAll('"', "").replace("[", "").replace("]", ""));
},
queryKey: ["shleves"],
});
const [addNew, setAddNew] = useState<boolean>(false);
return (
<Modal show={open} onHide={onClose} centered>
<ModalHeader
@ -48,23 +61,50 @@ export const MoveShelfModal = ({
}}
>
<Form.Group as={Row} className='mb-2'>
<Col sm='2'>
<Form.Label>Shelf</Form.Label>
<Col className='d-flex' sm='2'>
<Form.Label className='m-auto'>Shelf</Form.Label>
</Col>
<Col>
<Form.Select
{...register("target", { required: true })}
isInvalid={!!formState.errors.target}
>
{Object.keys(bookShelfs).map((key) => (
<option
key='key'
value={bookShelfs[key as keyof typeof bookShelfs]}
{!isLoading &&
(addNew ? (
<div className='d-flex'>
<Form.Control
id='move-shelf-text-input'
{...register("target", { required: true })}
/>
<Button
style={{
background: "transparent",
marginLeft: "0px",
marginRight: "10px",
}}
className='d-flex m-auto'
onClick={() => setAddNew(false)}
>
<ImCancelCircle className='m-auto primary' size={20} />
</Button>
</div>
) : (
<Form.Select
id='move-shelf-select'
{...register("target", {
required: true,
onChange: (ev) => {
if (ev.target.value === "addNew") {
setAddNew(true);
}
},
})}
isInvalid={!!formState.errors.target}
>
{key}
</option>
{shelves?.map((shelf) => (
<option key='key' value={shelf}>
{shelf}
</option>
))}
<option value='addNew'>Add new</option>
</Form.Select>
))}
</Form.Select>
<Form.Control.Feedback type='invalid'>
{!values.target
? "Target shelf is required"