diff --git a/.vscode/settings.json b/.vscode/settings.json
index 408e260..8a9c2ec 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,3 @@
{
- "cSpell.words": ["superjson", "trpc", "TRPC"]
+ "cSpell.words": ["healthcheck", "Healthcheck", "superjson", "trpc", "TRPC"]
}
diff --git a/src/contexts/create-new-deck/create-new-deck.context.tsx b/src/contexts/create-new-deck/create-new-deck.context.tsx
index 086d8a2..eb5369a 100644
--- a/src/contexts/create-new-deck/create-new-deck.context.tsx
+++ b/src/contexts/create-new-deck/create-new-deck.context.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useState } from 'react'
+import React, { useContext, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
@@ -149,6 +149,13 @@ export function CreateNewDeckContextProvider(
},
)
+ /**
+ * Healthcheck to wake up the generate flash cards API
+ */
+ useEffect(() => {
+ fetch(`${env.NEXT_PUBLIC_BRISKLY_GENERATE_FLASH_CARDS_API_URL}/healthcheck`)
+ }, [])
+
const addTopic = (topic: string) => {
setTopics(topics => [
...topics.filter(({ title }) => title !== topic),
diff --git a/src/modules/decks/components/study-session-card.component.tsx b/src/modules/decks/components/study-session-card.component.tsx
index 8fb4191..8db3ba1 100644
--- a/src/modules/decks/components/study-session-card.component.tsx
+++ b/src/modules/decks/components/study-session-card.component.tsx
@@ -26,12 +26,14 @@ export const StudySessionCard = (props: { deckId: string }) => {
const createStudySessionMutation = api.studySession.create.useMutation({
onSuccess: () => {
apiContext.decks.toBeReviewed.invalidate()
+ apiContext.decks.withStudySession.invalidate()
apiContext.studySession.getStudySessionBasicInfo.invalidate({ deckId })
},
})
const deleteStudySessionMutation = api.studySession.delete.useMutation({
onSuccess: () => {
apiContext.decks.toBeReviewed.invalidate()
+ apiContext.decks.withStudySession.invalidate()
apiContext.studySession.getStudySessionBasicInfo.invalidate({ deckId })
notify.success('Sessão de estudo deletada com sucesso!')
},
diff --git a/src/modules/decks/create/components/cards.component.tsx b/src/modules/decks/create/components/cards.component.tsx
index e7113be..2ec4c50 100644
--- a/src/modules/decks/create/components/cards.component.tsx
+++ b/src/modules/decks/create/components/cards.component.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import { useEffect, useState } from 'react'
import { PlusCircleIcon } from '@heroicons/react/24/outline'
@@ -14,6 +14,33 @@ type NewCardModalState = {
cardIdx?: number
}
+const loadingSteps = [
+ 'Coletando informações',
+ 'Analisando o contexto',
+ 'Gerando os cards',
+]
+
+const CYCLE_INTERVAL = 3000
+
+export const AiPoweredFlashCardsLoader = () => {
+ const [currentLoadingStep, setCurrentLoadingStep] = useState(0)
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setCurrentLoadingStep(state => (state + 1) % loadingSteps.length)
+ }, CYCLE_INTERVAL)
+
+ return () => clearInterval(interval)
+ }, [])
+
+ return (
+
+
+
{loadingSteps[currentLoadingStep]}
+
+ )
+}
+
export const Cards = () => {
const {
cards,
@@ -52,7 +79,7 @@ export const Cards = () => {
const renderAiCardsButton = () => {
const successContent = isGeneratingAiPoweredCards ? (
-
+
) : (
🤖
)
@@ -67,9 +94,7 @@ export const Cards = () => {
{hasErrorGeneratingAiPoweredCards ? errorContent : successContent}
-
+
)
diff --git a/src/modules/profile/components/decks-with-study-session.component.tsx b/src/modules/profile/components/decks-with-study-session.component.tsx
new file mode 100644
index 0000000..a3304d8
--- /dev/null
+++ b/src/modules/profile/components/decks-with-study-session.component.tsx
@@ -0,0 +1,22 @@
+import { DeckCardList } from '~/components/deck-card-list'
+import { api } from '~/utils/api'
+
+type UserDecksProps = {
+ userId: string
+ isVisible: boolean
+}
+
+export function DecksWithStudySession(props: UserDecksProps) {
+ const { isVisible } = props
+
+ const { data, isLoading, isError, refetch } =
+ api.decks.withStudySession.useQuery(undefined, { enabled: isVisible })
+
+ if (isLoading) return
+
+ if (isError) {
+ return
+ }
+
+ return
+}
diff --git a/src/modules/profile/constants/profile-menu-tabs.tsx b/src/modules/profile/constants/profile-menu-tabs.tsx
index b4d2e34..1868755 100644
--- a/src/modules/profile/constants/profile-menu-tabs.tsx
+++ b/src/modules/profile/constants/profile-menu-tabs.tsx
@@ -15,12 +15,23 @@ const UserFavoriteDecks = dynamic(() =>
),
)
+const DecksWithStudySession = dynamic(() =>
+ import('../components/decks-with-study-session.component').then(
+ module => module.DecksWithStudySession,
+ ),
+)
+
export const profileMenuTabs = [
{
name: 'Decks',
content: UserDecks,
isProfileOwnerOnly: false,
},
+ {
+ name: 'Meus Estudos',
+ content: DecksWithStudySession,
+ isProfileOwnerOnly: true,
+ },
{
name: 'Para Revisar',
content: DecksToBeReviewed,
diff --git a/src/pages/profile/[id].tsx b/src/pages/profile/[id].tsx
index 82add03..0941cca 100644
--- a/src/pages/profile/[id].tsx
+++ b/src/pages/profile/[id].tsx
@@ -111,13 +111,13 @@ const ProfilePage: WithAuthentication<
-
+
{tabs.map(tab => (
classNames(
- 'border-b-2 py-2 px-5 text-base font-medium leading-5 text-primary-900 ring-primary-50 ring-opacity-60 ring-offset-2 ring-offset-primary-500 focus:outline-none focus:ring-2',
+ 'min-w-max border-b-2 py-2 px-5 text-base font-medium leading-5 text-primary-900 ring-primary-50 ring-opacity-60 ring-offset-2 ring-offset-primary-500 focus:outline-none focus:ring-2',
selected ? 'border-primary-900' : 'border-primary-50',
)
}
diff --git a/src/server/api/routers/decks/decks.router.ts b/src/server/api/routers/decks/decks.router.ts
index 08496df..d0d025b 100644
--- a/src/server/api/routers/decks/decks.router.ts
+++ b/src/server/api/routers/decks/decks.router.ts
@@ -191,6 +191,32 @@ export const decksRouter = createTRPCRouter({
nextCursor,
}
}),
+ withStudySession: protectedProcedure.query(async ({ ctx }) => {
+ const { user } = ctx.session
+
+ const decks = await ctx.prisma.deck.findMany({
+ where: {
+ studySessions: {
+ some: {
+ userId: user.id,
+ },
+ },
+ },
+ orderBy: { createdAt: 'desc' },
+ include: {
+ favorites: true,
+ },
+ })
+
+ return decks.map(deck => ({
+ ...deck,
+ image: getS3ImageUrl(deck.image),
+ favorites: deck.favorites.length,
+ isFavorite: deck.favorites
+ .map(favorite => favorite.userId)
+ .includes(user.id),
+ }))
+ }),
byUser: protectedProcedure
.input(z.object({ userId: z.string() }))
.query(async ({ input: { userId }, ctx }) => {
@@ -218,6 +244,7 @@ export const decksRouter = createTRPCRouter({
.includes(signedInUser.id),
}))
}),
+
addFavorite: protectedProcedure
.input(z.object({ deckId: z.string() }))
.mutation(async ({ input: { deckId }, ctx }) => {