Adding in flashcard's and decks

This commit is contained in:
2026-06-25 02:39:16 -07:00
parent 78da882bda
commit 535c4c4943
34 changed files with 1248 additions and 1 deletions
+63
View File
@@ -0,0 +1,63 @@
import http from "@/api/http-client"
import { type FiltersOptions, type ModelOrder, type Policy, type WhereOptions } from "@/api/base-api"
export type FlashcardDeck = {
id: number
parentDeckId: number | null
creatorId: number
name: string
createdAt: string
updatedAt: string
}
export type FlashcardDeckWhereOptions = WhereOptions<FlashcardDeck, "creatorId" | "parentDeckId">
export type FlashcardDeckFiltersOptions = FiltersOptions<{
search: string | string[]
}>
export type FlashcardDeckQueryOptions = {
where?: FlashcardDeckWhereOptions
filters?: FlashcardDeckFiltersOptions
order?: ModelOrder[]
page?: number
perPage?: number
}
export const flashcardDecksApi = {
async list(params: FlashcardDeckQueryOptions = {}): Promise<{
flashcardDecks: FlashcardDeck[]
totalCount: number
}> {
const { data } = await http.get("/api/flashcard-decks", { params })
return data
},
async get(flashcardDeckId: number): Promise<{
flashcardDeck: FlashcardDeck
policy: Policy
}> {
const { data } = await http.get(`/api/flashcard-decks/${flashcardDeckId}`)
return data
},
async create(attributes: Partial<FlashcardDeck>): Promise<{
flashcardDeck: FlashcardDeck
}> {
const { data } = await http.post("/api/flashcard-decks", attributes)
return data
},
async update(
flashcardDeckId: number,
attributes: Partial<FlashcardDeck>
): Promise<{
flashcardDeck: FlashcardDeck
}> {
const { data } = await http.patch(`/api/flashcard-decks/${flashcardDeckId}`, attributes)
return data
},
async delete(flashcardDeckId: number): Promise<void> {
const { data } = await http.delete(`/api/flashcard-decks/${flashcardDeckId}`)
return data
},
}
export default flashcardDecksApi
+65
View File
@@ -0,0 +1,65 @@
import http from "@/api/http-client"
import { type FiltersOptions, type ModelOrder, type Policy, type WhereOptions } from "@/api/base-api"
export type Flashcard = {
id: number
flashcardDeckId: number
creatorId: number
cardType: string
front: string
back: string | null
createdAt: string
updatedAt: string
}
export type FlashcardWhereOptions = WhereOptions<Flashcard, "flashcardDeckId" | "creatorId" | "cardType">
export type FlashcardFiltersOptions = FiltersOptions<{
search: string | string[]
}>
export type FlashcardQueryOptions = {
where?: FlashcardWhereOptions
filters?: FlashcardFiltersOptions
order?: ModelOrder[]
page?: number
perPage?: number
}
export const flashcardsApi = {
async list(params: FlashcardQueryOptions = {}): Promise<{
flashcards: Flashcard[]
totalCount: number
}> {
const { data } = await http.get("/api/flashcards", { params })
return data
},
async get(flashcardId: number): Promise<{
flashcard: Flashcard
policy: Policy
}> {
const { data } = await http.get(`/api/flashcards/${flashcardId}`)
return data
},
async create(attributes: Partial<Flashcard>): Promise<{
flashcard: Flashcard
}> {
const { data } = await http.post("/api/flashcards", attributes)
return data
},
async update(
flashcardId: number,
attributes: Partial<Flashcard>
): Promise<{
flashcard: Flashcard
}> {
const { data } = await http.patch(`/api/flashcards/${flashcardId}`, attributes)
return data
},
async delete(flashcardId: number): Promise<void> {
const { data } = await http.delete(`/api/flashcards/${flashcardId}`)
return data
},
}
export default flashcardsApi
+87
View File
@@ -0,0 +1,87 @@
import { type Ref, reactive, toRefs, unref, watch } from "vue"
import { isNil } from "lodash"
import { type Policy } from "@/api/base-api"
import flashcardDecksApi, { type FlashcardDeck } from "@/api/flashcard-decks-api"
export { type FlashcardDeck }
export function useFlashcardDeck(id: Ref<number | null | undefined>) {
const state = reactive<{
flashcardDeck: FlashcardDeck | null
policy: Policy | null
isLoading: boolean
isErrored: boolean
}>({
flashcardDeck: null,
policy: null,
isLoading: false,
isErrored: false,
})
async function fetch(): Promise<FlashcardDeck> {
const staticId = unref(id)
if (isNil(staticId)) {
throw new Error("id is required")
}
state.isLoading = true
try {
const { flashcardDeck, policy } = await flashcardDecksApi.get(staticId)
state.isErrored = false
state.flashcardDeck = flashcardDeck
state.policy = policy
return flashcardDeck
} catch (error) {
console.error("Failed to fetch flashcard deck:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
async function save(): Promise<FlashcardDeck> {
const staticId = unref(id)
if (isNil(staticId)) {
throw new Error("id is required")
}
if (isNil(state.flashcardDeck)) {
throw new Error("No flashcard deck to save")
}
state.isLoading = true
try {
const { flashcardDeck } = await flashcardDecksApi.update(staticId, state.flashcardDeck)
state.isErrored = false
state.flashcardDeck = flashcardDeck
return flashcardDeck
} catch (error) {
console.error("Failed to save flashcard deck:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
watch(
() => unref(id),
async (newId) => {
if (isNil(newId)) return
await fetch()
},
{ immediate: true }
)
return {
...toRefs(state),
fetch,
refresh: fetch,
save,
}
}
export default useFlashcardDeck
+67
View File
@@ -0,0 +1,67 @@
import { type Ref, reactive, toRefs, ref, unref, watch } from "vue"
import flashcardDecksApi, {
type FlashcardDeck,
type FlashcardDeckWhereOptions,
type FlashcardDeckFiltersOptions,
type FlashcardDeckQueryOptions,
} from "@/api/flashcard-decks-api"
export {
type FlashcardDeck,
type FlashcardDeckWhereOptions,
type FlashcardDeckFiltersOptions,
type FlashcardDeckQueryOptions,
}
export function useFlashcardDecks(
queryOptions: Ref<FlashcardDeckQueryOptions> = ref({}),
{ skipWatchIf = () => false }: { skipWatchIf?: () => boolean } = {}
) {
const state = reactive<{
flashcardDecks: FlashcardDeck[]
totalCount: number
isLoading: boolean
isErrored: boolean
}>({
flashcardDecks: [],
totalCount: 0,
isLoading: false,
isErrored: false,
})
async function fetch(): Promise<FlashcardDeck[]> {
state.isLoading = true
try {
const { flashcardDecks, totalCount } = await flashcardDecksApi.list(unref(queryOptions))
state.isErrored = false
state.flashcardDecks = flashcardDecks
state.totalCount = totalCount
return flashcardDecks
} catch (error) {
console.error("Failed to fetch flashcard decks:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
watch(
() => [skipWatchIf(), unref(queryOptions)],
async ([skip]) => {
if (skip) return
await fetch()
},
{ deep: true, immediate: true }
)
return {
...toRefs(state),
fetch,
refresh: fetch,
}
}
export default useFlashcardDecks
+87
View File
@@ -0,0 +1,87 @@
import { type Ref, reactive, toRefs, unref, watch } from "vue"
import { isNil } from "lodash"
import { type Policy } from "@/api/base-api"
import flashcardsApi, { type Flashcard } from "@/api/flashcards-api"
export { type Flashcard }
export function useFlashcard(id: Ref<number | null | undefined>) {
const state = reactive<{
flashcard: Flashcard | null
policy: Policy | null
isLoading: boolean
isErrored: boolean
}>({
flashcard: null,
policy: null,
isLoading: false,
isErrored: false,
})
async function fetch(): Promise<Flashcard> {
const staticId = unref(id)
if (isNil(staticId)) {
throw new Error("id is required")
}
state.isLoading = true
try {
const { flashcard, policy } = await flashcardsApi.get(staticId)
state.isErrored = false
state.flashcard = flashcard
state.policy = policy
return flashcard
} catch (error) {
console.error("Failed to fetch flashcard:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
async function save(): Promise<Flashcard> {
const staticId = unref(id)
if (isNil(staticId)) {
throw new Error("id is required")
}
if (isNil(state.flashcard)) {
throw new Error("No flashcard to save")
}
state.isLoading = true
try {
const { flashcard } = await flashcardsApi.update(staticId, state.flashcard)
state.isErrored = false
state.flashcard = flashcard
return flashcard
} catch (error) {
console.error("Failed to save flashcard:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
watch(
() => unref(id),
async (newId) => {
if (isNil(newId)) return
await fetch()
},
{ immediate: true }
)
return {
...toRefs(state),
fetch,
refresh: fetch,
save,
}
}
export default useFlashcard
+67
View File
@@ -0,0 +1,67 @@
import { type Ref, reactive, toRefs, ref, unref, watch } from "vue"
import flashcardsApi, {
type Flashcard,
type FlashcardWhereOptions,
type FlashcardFiltersOptions,
type FlashcardQueryOptions,
} from "@/api/flashcards-api"
export {
type Flashcard,
type FlashcardWhereOptions,
type FlashcardFiltersOptions,
type FlashcardQueryOptions,
}
export function useFlashcards(
queryOptions: Ref<FlashcardQueryOptions> = ref({}),
{ skipWatchIf = () => false }: { skipWatchIf?: () => boolean } = {}
) {
const state = reactive<{
flashcards: Flashcard[]
totalCount: number
isLoading: boolean
isErrored: boolean
}>({
flashcards: [],
totalCount: 0,
isLoading: false,
isErrored: false,
})
async function fetch(): Promise<Flashcard[]> {
state.isLoading = true
try {
const { flashcards, totalCount } = await flashcardsApi.list(unref(queryOptions))
state.isErrored = false
state.flashcards = flashcards
state.totalCount = totalCount
return flashcards
} catch (error) {
console.error("Failed to fetch flashcards:", error)
state.isErrored = true
throw error
} finally {
state.isLoading = false
}
}
watch(
() => [skipWatchIf(), unref(queryOptions)],
async ([skip]) => {
if (skip) return
await fetch()
},
{ deep: true, immediate: true }
)
return {
...toRefs(state),
fetch,
refresh: fetch,
}
}
export default useFlashcards