mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-16 20:59:27 +00:00
wip
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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'
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user