This commit is contained in:
andres
2023-10-21 17:22:28 +02:00
parent 954d5b358f
commit 68e5977fb2
5 changed files with 184 additions and 172 deletions

View File

@@ -1,4 +1,5 @@
module.exports = { module.exports = {
...require('@it-incubator/prettier-config'), ...require('@it-incubator/prettier-config'),
tabWidth: 4,
//override settings here //override settings here
} }

View File

@@ -1,7 +1,7 @@
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Button, Card, ControlledTextField, Typography } from '../../ui' import { Button, Card, ControlledTextField, Typography } from '@/components'
import { DevTool } from '@hookform/devtools' import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod' import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod' import { z } from 'zod'

View File

@@ -6,26 +6,26 @@ import { Pagination } from '@/components/ui/pagination'
import { useGetDeckByIdQuery, useGetDeckCardsQuery } from '@/services' import { useGetDeckByIdQuery, useGetDeckCardsQuery } from '@/services'
export const DeckPage = () => { export const DeckPage = () => {
const { deckId } = useParams() const { deckId } = useParams()
const [currentPage, setCurrentPage] = useState(1) const [currentPage, setCurrentPage] = useState(1)
const { data: deckData } = useGetDeckByIdQuery({ id: deckId || '' }) const { data: deckData } = useGetDeckByIdQuery({ id: deckId || '' })
const { data: cardsData } = useGetDeckCardsQuery({ id: deckId || '' }) const { data: cardsData } = useGetDeckCardsQuery({ id: deckId || '' })
const learnLink = `/decks/${deckId}/learn` const learnLink = `/decks/${deckId}/learn`
return ( return (
<div> <div>
<Typography variant={'large'}>{deckData?.name}</Typography> <Typography variant={'large'}>{deckData?.name}</Typography>
<Button as={Link} to={learnLink}> <Button as={Link} to={learnLink}>
Learn Learn
</Button> </Button>
<TextField placeholder={'Search cards'} search /> <TextField placeholder={'Search cards'} search />
<CardsTable cards={cardsData?.items} /> <CardsTable cards={cardsData?.items} />
<Pagination <Pagination
count={cardsData?.pagination?.totalPages || 1} count={cardsData?.pagination?.totalPages || 1}
onChange={setCurrentPage} onChange={setCurrentPage}
page={currentPage} page={currentPage}
/> />
</div> </div>
) )
} }

View File

@@ -7,18 +7,18 @@ import { DeleteDeckDialog } from '@/components/decks/delete-deck-dialog'
import { Pagination } from '@/components/ui/pagination' import { Pagination } from '@/components/ui/pagination'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { import {
Tab, Tab,
useCreateDeckMutation, useCreateDeckMutation,
useDeleteDeckMutation, useDeleteDeckMutation,
useGetDecksQuery, useGetDecksQuery,
useUpdateDeckMutation, useUpdateDeckMutation,
} from '@/services/decks' } from '@/services/decks'
import { import {
selectDecksCurrentPage, selectDecksCurrentPage,
selectDecksCurrentTab, selectDecksCurrentTab,
selectDecksMaxCards, selectDecksMaxCards,
selectDecksMinCards, selectDecksMinCards,
selectDecksSearch, selectDecksSearch,
} from '@/services/decks/decks.selectors' } from '@/services/decks/decks.selectors'
import { decksSlice } from '@/services/decks/decks.slice' import { decksSlice } from '@/services/decks/decks.slice'
import { useAppDispatch, useAppSelector } from '@/services/store' import { useAppDispatch, useAppSelector } from '@/services/store'
@@ -26,127 +26,132 @@ import { useAppDispatch, useAppSelector } from '@/services/store'
import s from './decks-page.module.scss' import s from './decks-page.module.scss'
export const DecksPage = () => { export const DecksPage = () => {
const [showCreateModal, setShowCreateModal] = useState(false) const [showCreateModal, setShowCreateModal] = useState(false)
const [deckToDeleteId, setDeckToDeleteId] = useState<null | string>(null) const [deckToDeleteId, setDeckToDeleteId] = useState<null | string>(null)
const [deckToEditId, setDeckToEditId] = useState<null | string>(null) const [deckToEditId, setDeckToEditId] = useState<null | string>(null)
const showEditModal = !!deckToEditId const showEditModal = !!deckToEditId
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const currentPage = useAppSelector(selectDecksCurrentPage) const currentPage = useAppSelector(selectDecksCurrentPage)
const minCards = useAppSelector(selectDecksMinCards) const minCards = useAppSelector(selectDecksMinCards)
const maxCards = useAppSelector(selectDecksMaxCards) const maxCards = useAppSelector(selectDecksMaxCards)
const currentTab = useAppSelector(selectDecksCurrentTab) const currentTab = useAppSelector(selectDecksCurrentTab)
const search = useAppSelector(selectDecksSearch) const search = useAppSelector(selectDecksSearch)
const setCurrentPage = (page: number) => dispatch(decksSlice.actions.setCurrentPage(page)) const setCurrentPage = (page: number) => dispatch(decksSlice.actions.setCurrentPage(page))
const setMinCards = (minCards: number) => dispatch(decksSlice.actions.setMinCards(minCards)) const setMinCards = (minCards: number) => dispatch(decksSlice.actions.setMinCards(minCards))
const setMaxCards = (maxCards: number) => dispatch(decksSlice.actions.setMaxCards(maxCards)) const setMaxCards = (maxCards: number) => dispatch(decksSlice.actions.setMaxCards(maxCards))
const setSearch = (search: string) => dispatch(decksSlice.actions.setSearch(search)) const setSearch = (search: string) => dispatch(decksSlice.actions.setSearch(search))
const setCurrentTab = (tab: Tab) => dispatch(decksSlice.actions.setCurrentTab(tab)) const setCurrentTab = (tab: Tab) => dispatch(decksSlice.actions.setCurrentTab(tab))
const resetFilters = () => { const resetFilters = () => {
dispatch(decksSlice.actions.resetFilters()) dispatch(decksSlice.actions.resetFilters())
setRangeValue([0, decks?.maxCardsCount || undefined]) setRangeValue([0, decks?.maxCardsCount || undefined])
} }
const [rangeValue, setRangeValue] = useState([minCards, maxCards]) const [rangeValue, setRangeValue] = useState([minCards, maxCards])
const handleSliderCommitted = (value: number[]) => { const handleSliderCommitted = (value: number[]) => {
setMinCards(value[0]) setMinCards(value[0])
setMaxCards(value[1]) setMaxCards(value[1])
} }
const currentUserId = 'f2be95b9-4d07-4751-a775-bd612fc9553a' const currentUserId = 'f2be95b9-4d07-4751-a775-bd612fc9553a'
const authorId = currentTab === 'my' ? currentUserId : undefined const authorId = currentTab === 'my' ? currentUserId : undefined
const { data: decks } = useGetDecksQuery({ const { data: decks } = useGetDecksQuery({
authorId, authorId,
currentPage, currentPage,
maxCardsCount: maxCards, maxCardsCount: maxCards,
minCardsCount: minCards, minCardsCount: minCards,
name: search, name: search,
}) })
const showConfirmDeleteModal = !!deckToDeleteId const showConfirmDeleteModal = !!deckToDeleteId
const deckToDeleteName = decks?.items?.find(deck => deck.id === deckToDeleteId)?.name const deckToDeleteName = decks?.items?.find(deck => deck.id === deckToDeleteId)?.name
const deckToEdit = decks?.items?.find(deck => deck.id === deckToEditId) const deckToEdit = decks?.items?.find(deck => deck.id === deckToEditId)
const [createDeck] = useCreateDeckMutation() const [createDeck] = useCreateDeckMutation()
const [deleteDeck] = useDeleteDeckMutation() const [deleteDeck] = useDeleteDeckMutation()
const [updateDeck] = useUpdateDeckMutation() const [updateDeck] = useUpdateDeckMutation()
const openCreateModal = () => setShowCreateModal(true) const openCreateModal = () => setShowCreateModal(true)
if (!decks) { if (!decks) {
return <div>loading...</div> return <div>loading...</div>
} }
return ( return (
<Page> <Page>
<DeleteDeckDialog <DeleteDeckDialog
deckName={deckToDeleteName ?? 'Selected deck'} deckName={deckToDeleteName ?? 'Selected deck'}
onCancel={() => setDeckToDeleteId(null)} onCancel={() => setDeckToDeleteId(null)}
onConfirm={() => { onConfirm={() => {
deleteDeck({ id: deckToDeleteId ?? '' }) deleteDeck({ id: deckToDeleteId ?? '' })
setDeckToDeleteId(null) setDeckToDeleteId(null)
}} }}
onOpenChange={() => setDeckToDeleteId(null)} onOpenChange={() => setDeckToDeleteId(null)}
open={showConfirmDeleteModal} open={showConfirmDeleteModal}
/> />
<DeckDialog <DeckDialog
defaultValues={deckToEdit} defaultValues={deckToEdit}
key={deckToEditId} key={deckToEditId}
onConfirm={data => { onConfirm={data => {
if (!deckToEditId) { if (!deckToEditId) {
return return
} }
updateDeck({ id: deckToEditId, ...data }) updateDeck({ id: deckToEditId, ...data })
}} }}
onOpenChange={() => setDeckToEditId(null)} onOpenChange={() => setDeckToEditId(null)}
open={showEditModal} open={showEditModal}
/> />
<div className={s.root}> <div className={s.root}>
<div className={s.header}> <div className={s.header}>
<Typography variant={'large'}>Decks</Typography> <Typography variant={'large'}>Decks</Typography>
<Button onClick={openCreateModal}>Add new deck</Button> <Button onClick={openCreateModal}>Add new deck</Button>
<DeckDialog <DeckDialog
onCancel={() => setShowCreateModal(false)} onCancel={() => setShowCreateModal(false)}
onConfirm={createDeck} onConfirm={createDeck}
onOpenChange={setShowCreateModal} onOpenChange={setShowCreateModal}
open={showCreateModal} open={showCreateModal}
/> />
</div> </div>
<div className={s.filters}> <div className={s.filters}>
<TextField onValueChange={setSearch} placeholder={'Search'} search value={search} /> <TextField
<Tabs onValueChange={value => setCurrentTab(value as Tab)} value={currentTab}> onValueChange={setSearch}
<TabsList> placeholder={'Search'}
<TabsTrigger value={'my'}>My decks</TabsTrigger> search
<TabsTrigger value={'all'}>All decks</TabsTrigger> value={search}
</TabsList> />
</Tabs> <Tabs onValueChange={value => setCurrentTab(value as Tab)} value={currentTab}>
<Slider <TabsList>
max={decks?.maxCardsCount || 0} <TabsTrigger value={'my'}>My decks</TabsTrigger>
min={0} <TabsTrigger value={'all'}>All decks</TabsTrigger>
onValueChange={setRangeValue} </TabsList>
onValueCommit={handleSliderCommitted} </Tabs>
value={rangeValue} <Slider
/> max={decks?.maxCardsCount || 0}
<Button onClick={resetFilters} variant={'secondary'}> min={0}
Clear filters onValueChange={setRangeValue}
</Button> onValueCommit={handleSliderCommitted}
</div> value={rangeValue}
<DecksTable />
currentUserId={currentUserId} <Button onClick={resetFilters} variant={'secondary'}>
decks={decks?.items} Clear filters
onDeleteClick={setDeckToDeleteId} </Button>
onEditClick={setDeckToEditId} </div>
/> <DecksTable
<Pagination currentUserId={currentUserId}
count={decks?.pagination?.totalPages || 1} decks={decks?.items}
onChange={setCurrentPage} onDeleteClick={setDeckToDeleteId}
page={currentPage} onEditClick={setDeckToEditId}
/> />
</div> <Pagination
</Page> count={decks?.pagination?.totalPages || 1}
) onChange={setCurrentPage}
page={currentPage}
/>
</div>
</Page>
)
} }

View File

@@ -6,19 +6,31 @@ import {
GetDecksArgs, GetDecksArgs,
UpdateDeckArgs, UpdateDeckArgs,
} from './decks.types' } from './decks.types'
import { baseApi } from '@/services' import { baseApi } from '@/services'
const decksService = baseApi.injectEndpoints({ const decksService = baseApi.injectEndpoints({
endpoints: builder => ({ endpoints: builder => ({
getDecks: builder.query<DecksResponse, GetDecksArgs | void>({ createDeck: builder.mutation<DeckResponse, CreateDeckArgs>({
query: args => { invalidatesTags: ['Decks'],
return { onQueryStarted: async (_, { dispatch, getCacheEntry, getState, queryFulfilled }) => {
url: `v1/decks`, const data = getCacheEntry()
params: args ?? undefined, const state = getState()
}
decksService.util.re
await queryFulfilled
}, },
providesTags: ['Decks'], query: body => ({
body,
method: 'POST',
url: `v1/decks`,
}),
}),
deleteDeck: builder.mutation<void, { id: string }>({
invalidatesTags: ['Decks'],
query: ({ id }) => ({
method: 'DELETE',
url: `v1/decks/${id}`,
}),
}), }),
getDeckById: builder.query<DeckResponse, { id: string }>({ getDeckById: builder.query<DeckResponse, { id: string }>({
query: ({ id }) => `v1/decks/${id}`, query: ({ id }) => `v1/decks/${id}`,
@@ -26,37 +38,31 @@ const decksService = baseApi.injectEndpoints({
getDeckCards: builder.query<CardsResponse, { id: string }>({ getDeckCards: builder.query<CardsResponse, { id: string }>({
query: ({ id }) => `v1/decks/${id}/cards`, query: ({ id }) => `v1/decks/${id}/cards`,
}), }),
createDeck: builder.mutation<DeckResponse, CreateDeckArgs>({ getDecks: builder.query<DecksResponse, GetDecksArgs | void>({
query: body => ({ providesTags: ['Decks'],
url: `v1/decks`, query: args => {
method: 'POST', return {
body, params: args ?? undefined,
}), url: `v1/decks`,
invalidatesTags: ['Decks'], }
}), },
deleteDeck: builder.mutation<void, { id: string }>({
query: ({ id }) => ({
url: `v1/decks/${id}`,
method: 'DELETE',
}),
invalidatesTags: ['Decks'],
}), }),
updateDeck: builder.mutation<DeckResponse, UpdateDeckArgs>({ updateDeck: builder.mutation<DeckResponse, UpdateDeckArgs>({
query: ({ id, ...body }) => ({
url: `v1/decks/${id}`,
method: 'PATCH',
body,
}),
invalidatesTags: ['Decks'], invalidatesTags: ['Decks'],
query: ({ id, ...body }) => ({
body,
method: 'PATCH',
url: `v1/decks/${id}`,
}),
}), }),
}), }),
}) })
export const { export const {
useGetDecksQuery,
useGetDeckByIdQuery,
useGetDeckCardsQuery,
useCreateDeckMutation, useCreateDeckMutation,
useDeleteDeckMutation, useDeleteDeckMutation,
useGetDeckByIdQuery,
useGetDeckCardsQuery,
useGetDecksQuery,
useUpdateDeckMutation, useUpdateDeckMutation,
} = decksService } = decksService