From edb8cc43ae3bd06073c7887db9d30576d7b8f361 Mon Sep 17 00:00:00 2001 From: safronman Date: Sat, 15 Jun 2024 12:15:56 +0300 Subject: [PATCH] 3 - file final --- .../decks/deck-dialog/deck-dialog.tsx | 63 +++++++++++++++++-- src/components/decks/decks-table.tsx | 9 +++ src/pages/decks-page/decks-page.tsx | 1 + src/services/decks/decks.service.ts | 52 ++++++++++++--- src/services/decks/decks.types.ts | 4 +- src/services/flashCardsBaseQuery.ts | 2 - 6 files changed, 111 insertions(+), 20 deletions(-) diff --git a/src/components/decks/deck-dialog/deck-dialog.tsx b/src/components/decks/deck-dialog/deck-dialog.tsx index a1d1ee0..7ec9caf 100644 --- a/src/components/decks/deck-dialog/deck-dialog.tsx +++ b/src/components/decks/deck-dialog/deck-dialog.tsx @@ -1,6 +1,7 @@ +import { ChangeEvent, useEffect, useState } from 'react' import { useForm } from 'react-hook-form' -import { ControlledCheckbox, ControlledTextField, Dialog, DialogProps } from '@/components' +import { Button, ControlledCheckbox, ControlledTextField, Dialog, DialogProps } from '@/components' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' @@ -13,33 +14,83 @@ const newDeckSchema = z.object({ type FormValues = z.infer -type Props = Pick & { - defaultValues?: FormValues - onConfirm: (data: FormValues) => void -} +type Props = { + defaultValues?: { cover?: null | string } & FormValues + onConfirm: (data: { cover?: File | null } & FormValues) => void +} & Pick + export const DeckDialog = ({ defaultValues = { isPrivate: false, name: '' }, onCancel, onConfirm, ...dialogProps }: Props) => { + const [cover, setCover] = useState(null) + const [preview, setPreview] = useState('') + + useEffect(() => { + if (defaultValues?.cover) { + setPreview(defaultValues?.cover) + } + }, [defaultValues?.cover]) + + useEffect(() => { + if (cover) { + // создать ссылку на файл + const newPreview = URL.createObjectURL(cover) + + // зачищаем старое превью чтобы не хранилось в памяти + if (preview) { + URL.revokeObjectURL(preview) + } + setPreview(newPreview) + + // зачищаем новое превью чтобы не хранилось в памяти + return () => URL.revokeObjectURL(newPreview) + } + }, [cover]) + const { control, handleSubmit, reset } = useForm({ defaultValues, resolver: zodResolver(newDeckSchema), }) const onSubmit = handleSubmit(data => { - onConfirm(data) + onConfirm({ ...data, cover }) dialogProps.onOpenChange?.(false) reset() }) + const handleCancel = () => { reset() onCancel?.() } + const uploadHandler = (e: ChangeEvent) => { + if (e.target.files && e.target.files.length) { + const file = e.target.files[0] + + setCover(file) + } + } + + const removeCoverHandler = () => { + setCover(null) + setPreview(null) + } + return (
+ {preview && {'cover'}} + + + + {preview && ( + + )} + {decks?.map(deck => ( + + {deck.cover && {'cover'}} + {!deck.cover && } + {deck.name} diff --git a/src/pages/decks-page/decks-page.tsx b/src/pages/decks-page/decks-page.tsx index c59b29f..3fe2204 100644 --- a/src/pages/decks-page/decks-page.tsx +++ b/src/pages/decks-page/decks-page.tsx @@ -47,6 +47,7 @@ export const DecksPage = () => { const { currentData: decksCurrentData, data: decksData } = useGetDecksQuery({ authorId, currentPage, + itemsPerPage: 3, maxCardsCount, minCardsCount, name: search, diff --git a/src/services/decks/decks.service.ts b/src/services/decks/decks.service.ts index c014266..a44f3ea 100644 --- a/src/services/decks/decks.service.ts +++ b/src/services/decks/decks.service.ts @@ -14,11 +14,26 @@ const decksService = flashcardsApi.injectEndpoints({ endpoints: builder => ({ createDeck: builder.mutation({ invalidatesTags: ['Decks'], - query: body => ({ - body, - method: 'POST', - url: `v1/decks`, - }), + query: body => { + const { cover, isPrivate, name } = body + + const formData = new FormData() + + formData.append('name', name) + + if (isPrivate) { + formData.append('isPrivate', isPrivate.toString()) + } + if (cover) { + formData.append('cover', cover) + } + + return { + body: formData, + method: 'POST', + url: `v1/decks`, + } + }, }), deleteDeck: builder.mutation({ invalidatesTags: ['Decks'], @@ -44,11 +59,28 @@ const decksService = flashcardsApi.injectEndpoints({ }), updateDeck: builder.mutation({ invalidatesTags: ['Decks'], - query: ({ id, ...body }) => ({ - body, - method: 'PATCH', - url: `v1/decks/${id}`, - }), + query: ({ cover, id, isPrivate, name }) => { + const formData = new FormData() + + if (name) { + formData.append('name', name) + } + if (isPrivate) { + formData.append('isPrivate', isPrivate.toString()) + } + if (cover) { + formData.append('cover', cover) + } else if (cover === null) { + // небходимо для зануления картинки + formData.append('cover', '') + } + + return { + body: formData, + method: 'PATCH', + url: `v1/decks/${id}`, + } + }, }), }), }) diff --git a/src/services/decks/decks.types.ts b/src/services/decks/decks.types.ts index 777c54c..a01d388 100644 --- a/src/services/decks/decks.types.ts +++ b/src/services/decks/decks.types.ts @@ -64,11 +64,11 @@ export type GetDecksArgs = { } export type CreateDeckArgs = { - cover?: string + cover?: File | null isPrivate?: boolean name: string } -export type UpdateDeckArgs = Partial & { id: Deck['id'] } +export type UpdateDeckArgs = { id: Deck['id'] } & Partial export type Tab = 'all' | 'my' diff --git a/src/services/flashCardsBaseQuery.ts b/src/services/flashCardsBaseQuery.ts index c47c5be..d7395c5 100644 --- a/src/services/flashCardsBaseQuery.ts +++ b/src/services/flashCardsBaseQuery.ts @@ -55,8 +55,6 @@ export const baseQueryWithReauth: BaseQueryFn< extraOptions )) as any - console.log('refreshResult', refreshResult) - if (refreshResult.data) { localStorage.setItem('accessToken', refreshResult.data.accessToken) localStorage.setItem('refreshToken', refreshResult.data.refreshToken)