-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9e002ef
commit 30a2e3d
Showing
4 changed files
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import Generate from "@/components/core/generate"; | ||
|
||
export default function GeneratePage() { | ||
return ( | ||
// <div className="page-container flex items-center min-h-screen"> | ||
// <h1 className='text-3xl text-foreground font-semibold'>Generate Flashcards</h1> | ||
// <Generate /> | ||
// </div> | ||
<div className="min-h-screen"> | ||
<Generate /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// app/flashcards/[id]/page.tsx | ||
|
||
'use client'; | ||
|
||
import { useEffect, useState } from 'react'; | ||
import { useRouter, useParams } from 'next/navigation'; | ||
import { deleteFlashcardSet, fetchFlashcardsBySet } from '@/firebase/firestore/utils'; | ||
import { Button } from '@/components/ui/button'; | ||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | ||
import { RefreshCw, ArrowRight, ArrowLeft, ChevronLeft, Loader2, Trash } from 'lucide-react'; | ||
import type { Flashcard } from '@/types'; | ||
import { useUser } from '@clerk/clerk-react'; | ||
|
||
export default function FlashcardSetPage() { | ||
const [flashcards, setFlashcards] = useState<Flashcard[]>([]); | ||
const [loading, setLoading] = useState<boolean>(true); | ||
const [currentIndex, setCurrentIndex] = useState<number>(0); | ||
const [flip, setFlip] = useState<boolean>(false); | ||
const router = useRouter(); | ||
const { id } = useParams(); | ||
const { user, isLoaded } = useUser(); // Add isLoaded to check if user data is loaded | ||
|
||
useEffect(() => { | ||
if (id && isLoaded && user) { | ||
const fetchData = async () => { | ||
try { | ||
const userId = user.id; // Get the actual userId from Clerk | ||
const fetchedFlashcards = await fetchFlashcardsBySet(userId, id as string); | ||
setFlashcards(fetchedFlashcards); | ||
} catch (error) { | ||
console.error('Error fetching flashcards:', error); | ||
} finally { | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
fetchData(); | ||
} | ||
}, [id, isLoaded, user]); | ||
|
||
const currentCard = flashcards[currentIndex]; | ||
|
||
const handleFlip = () => setFlip(!flip); | ||
|
||
const handleNext = () => { | ||
setCurrentIndex((prevIndex) => (prevIndex + 1) % flashcards.length); | ||
setFlip(false); | ||
}; | ||
|
||
const handlePrevious = () => { | ||
setCurrentIndex((prevIndex) => (prevIndex - 1 + flashcards.length) % flashcards.length); | ||
setFlip(false); | ||
}; | ||
|
||
// Key Events | ||
useEffect(() => { | ||
const handleKeyDown = (event: { key: string; }) => { | ||
if (event.key === 'ArrowRight') { | ||
handleNext(); | ||
} else if (event.key === 'ArrowLeft') { | ||
handlePrevious(); | ||
} else if (event.key === ' ' || event.key === 'Enter') { | ||
handleFlip(); | ||
} | ||
}; | ||
|
||
document.addEventListener('keydown', handleKeyDown); | ||
|
||
// Cleanup function | ||
return () => { | ||
document.removeEventListener('keydown', handleKeyDown); | ||
}; | ||
},); | ||
|
||
if (loading) return ( | ||
<div className="min-h-screen flex justify-center"> | ||
<Loader2 className="w-16 h-16 animate-spin text-primary" /> | ||
</div> | ||
); | ||
|
||
const handleDeleteSet = async () => { | ||
try { | ||
if (isLoaded && user) { | ||
const userId = user.id; // Get the actual userId from Clerk | ||
await deleteFlashcardSet(userId, id as string); | ||
router.push('/flashcards'); // Redirect to flashcard sets page after deletion | ||
} | ||
} catch (error) { | ||
console.error('Error deleting flashcard set:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="min-h-screen p-4 relative page-container"> | ||
{/* Back button in top-left corner */} | ||
<Button | ||
variant="outline" | ||
onClick={() => router.push('/flashcards')} | ||
className="absolute top-4 left-4" | ||
size="icon" | ||
> | ||
<ChevronLeft className="h-6 w-6" /> | ||
</Button> | ||
<Button | ||
variant="outline" | ||
onClick={handleDeleteSet} | ||
className="absolute top-4 right-4" | ||
size="icon" | ||
> | ||
{/* <span>Delete Set</span> */} | ||
<Trash className="h-6 w-6" /> | ||
</Button> | ||
{flashcards.length > 0 ? ( | ||
<div className="flex flex-col items-center justify-center min-h-screen p-4"> | ||
<Card className="w-full max-w-3xl"> | ||
<CardHeader className="flex flex-row items-center justify-between"> | ||
<CardTitle>Flashcards</CardTitle> | ||
{currentCard && ( | ||
<p className="text-sm text-muted-foreground"> | ||
Card {currentIndex + 1} of {flashcards.length} | ||
</p> | ||
)} | ||
</CardHeader> | ||
<CardContent> | ||
<Card className="bg-secondary"> | ||
<CardContent className="p-6 h-64 flex flex-col justify-center items-center"> | ||
{currentCard ? ( | ||
<> | ||
<h2 className="text-xl font-semibold mb-4">{currentCard.question}</h2> | ||
{flip && <p className="text-muted-foreground mx-auto text-lg">{currentCard.answer}</p>} | ||
</> | ||
) : ( | ||
<p>No flashcards available.</p> | ||
)} | ||
</CardContent> | ||
</Card> | ||
<div className="flex justify-between mt-4"> | ||
<Button | ||
onClick={handleFlip} | ||
variant="ghost" | ||
size="icon" | ||
disabled={!currentCard} | ||
> | ||
<RefreshCw className="h-6 w-6" /> | ||
</Button> | ||
<div> | ||
<Button | ||
onClick={handlePrevious} | ||
variant="ghost" | ||
size="icon" | ||
disabled={!currentCard} | ||
> | ||
<ArrowLeft className="h-6 w-6" /> | ||
</Button> | ||
<Button | ||
onClick={handleNext} | ||
variant="ghost" | ||
size="icon" | ||
disabled={!currentCard} | ||
> | ||
<ArrowRight className="h-6 w-6" /> | ||
</Button> | ||
</div> | ||
</div> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
) : ( | ||
<p>No flashcards found for this set.</p> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
'use client' | ||
|
||
import { useRouter, useSearchParams } from 'next/navigation' | ||
import { useEffect, useState } from 'react' | ||
import { Card, CardContent } from "@/components/ui/card" | ||
import { Button } from "@/components/ui/button" | ||
import { Loader2 } from 'lucide-react' | ||
import Link from 'next/link' | ||
|
||
interface Session { | ||
payment_status: string; | ||
// Add other session properties as needed | ||
} | ||
|
||
export default function ResultPage() { | ||
const router = useRouter() | ||
const searchParams = useSearchParams() | ||
const session_id = searchParams.get('session_id') | ||
const [loading, setLoading] = useState(true) | ||
const [session, setSession] = useState<Session | null>(null) | ||
const [error, setError] = useState<string | null>(null) | ||
|
||
useEffect(() => { | ||
const fetchCheckoutSession = async () => { | ||
if (!session_id) return | ||
try { | ||
const res = await fetch(`/api/checkout-sessions?session_id=${session_id}`) | ||
const sessionData = await res.json() | ||
if (res.ok) { | ||
setSession(sessionData) | ||
} else { | ||
setError(sessionData.error) | ||
} | ||
} catch (err) { | ||
setError('An error occurred while retrieving the session.') | ||
} finally { | ||
setLoading(false) | ||
} | ||
} | ||
fetchCheckoutSession() | ||
}, [session_id]) | ||
|
||
if (loading) { | ||
return ( | ||
<div className="min-h-screen flex justify-center items-center"> | ||
<Loader2 className="w-16 h-16 animate-spin text-primary" /> | ||
</div> | ||
) | ||
} | ||
|
||
return ( | ||
<div className="min-h-screen p-8 max-w-5xl mx-auto"> | ||
<div className="flex justify-between items-center mb-6"> | ||
<h1 className="text-3xl font-bold">Payment Result</h1> | ||
<Link href="/flashcards"> | ||
<Button>Back to Flashcards</Button> | ||
</Link> | ||
</div> | ||
<Card className="lg:hover:scale-[1.02] duration-200 transition ease-in-out"> | ||
<CardContent className="p-6"> | ||
{error ? ( | ||
<p className="text-xl text-red-500">{error}</p> | ||
) : session?.payment_status === 'paid' ? ( | ||
<> | ||
<h2 className="text-2xl text-primary mb-4">Thank you for your purchase!</h2> | ||
<p className="text-lg mb-2">Session ID: {session_id}</p> | ||
<p className="text-muted-foreground"> | ||
We have received your payment. You will receive an email with the order details shortly. | ||
</p> | ||
</> | ||
) : ( | ||
<> | ||
<h2 className="text-2xl text-primary mb-4">Payment failed</h2> | ||
<p className="text-muted-foreground"> | ||
Your payment was not successful. Please try again. | ||
</p> | ||
</> | ||
)} | ||
</CardContent> | ||
</Card> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { SiteConfig } from "@/types"; | ||
|
||
const site_url = process.env.NEXT_PUBLIC_APP_URL; | ||
|
||
export const siteConfig: SiteConfig = { | ||
name: "AI Flashcards App", | ||
description: | ||
"AI Flashcards App is a flashcard app that uses OpenAI API to generate flashcards. Users can create flashcards based ona topic. The app is powered by Next.js and Tailwind CSS.", | ||
url: "site_url", | ||
ogImage: `${site_url}/_static/og.jpg`, | ||
links: { | ||
twitter: "https://twitter.com/00tush_", | ||
github: "https://github.com/tushcmd/ai-flashcards", | ||
portfolio: "https://tushdev.co", | ||
}, | ||
mailSupport: "muturidavid854@gmail.com", | ||
}; |