Add a flashcard system to the app where:
- Users can create, edit, and delete flashcard decks (e.g., "Biology Test Terms", "Memory Verses")
- Each deck contains multiple flashcards with front/back text
- Bot can create flashcards via tool calls during chat (optionally adding to a deck)
- Study mode uses flip card interface per deck
- Hamburger menu in header switches between Chat and Flashcards views
- Flashcard decks and flashcards stored per profile (child account)
- No limit on number of decks or cards per profile
- Manual entry: Users create decks and add cards with front/back text
- Auto-generate: Bot can create flashcards via tool call during chat
- Flip card animation to reveal answer
- Navigate between cards in a deck (prev/next)
- Select which deck to study
- Hamburger menu at top-left to switch between Chat list and Flashcards
File: back/bots/models/flashcard.py
class Deck(models.Model):
deck_id = models.UUIDField(default=uuid.uuid4, unique=True)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='decks')
chat = models.ForeignKey(Chat, on_delete=models.SET_NULL, null=True, blank=True, related_name='decks')
name = models.CharField(max_length=255)
description = models.TextField(blank=True, default="")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Flashcard(models.Model):
flashcard_id = models.UUIDField(default=uuid.uuid4, unique=True)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE, related_name='flashcards')
front = models.TextField()
back = models.TextField()
order = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=["deck", "order"], name="unique_flashcard_order_per_deck")
]
indexes = [
models.Index(fields=["deck", "order"])
]Add to back/bots/models/__init__.py
File: back/bots/serializers/flashcard_serializer.py
class FlashcardSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Flashcard
fields = ['id', 'flashcard_id', 'deck', 'front', 'back', 'order', 'created_at', 'updated_at']
class DeckSerializer(serializers.HyperlinkedModelSerializer):
flashcards = FlashcardSerializer(many=True, read_only=True)
card_count = serializers.SerializerMethodField()
class Meta:
model = Deck
fields = ['id', 'deck_id', 'profile', 'chat', 'name', 'description', 'flashcards', 'card_count', 'created_at', 'updated_at']
class DeckListSerializer(serializers.HyperlinkedModelSerializer):
card_count = serializers.SerializerMethodField()
class Meta:
model = Deck
fields = ['id', 'deck_id', 'name', 'description', 'card_count', 'created_at', 'updated_at']Add to back/bots/serializers/__init__.py
File: back/bots/viewsets/flashcard_viewset.py
GET /decks.json- List all decks for profilePOST /decks.json- Create new deckGET /decks/{deck_id}.json- Retrieve deck with all flashcardsPUT /decks/{deck_id}.json- Update deckDELETE /decks/{deck_id}.json- Delete deck (cascades to flashcards)
GET /decks/{deck_id}/flashcards.json- List cards in a deckPOST /decks/{deck_id}/flashcards.json- Add card to deckGET /decks/{deck_id}/flashcards/{flashcard_id}.json- Retrieve cardPUT /decks/{deck_id}/flashcards/{flashcard_id}.json- Update cardDELETE /decks/{deck_id}/flashcards/{flashcard_id}.json- Delete card
Filter decks by profile from query params. Permission: IsOwner.
Scope decks to profiles the authenticated user is authorized to access. If a profile filter is provided, validate it belongs to the authenticated user before applying it. Permission: enforce object-level ownership checks server-side (not query-param trust).
Annotate with card_count for list view.
File: back/server/urls.py
Add router registration for FlashcardViewSet.
File: back/bots/models/chat.py (inside existing agent tool-call loop)
Define tools with @tool and bind them alongside existing tools. Handle tool calls using LangChain tool_call["name"] and tool_call["args"]. When bot returns a tool call with name "create_flashcard" or "create_deck", parse and create Deck/Flashcard entries. Persist Deck/Flashcard in the tool implementation and return structured tool results.
Tool call format from bot (LangChain style):
{
"name": "create_flashcard_deck",
"args": {
"name": "Biology Test Terms",
"description": "Key terms for Chapter 5",
"flashcards": [
{"front": "What is photosynthesis?", "back": "The process by which plants convert light energy into chemical energy"},
{"front": "What is cellular respiration?", "back": "The process of converting glucose into ATP"}
]
},
"id": "call_abc123"
}Or single card:
{
"name": "create_flashcard",
"args": {
"deck_name": "Memory Verses",
"front": "John 3:16",
"back": "For God so loved the world..."
},
"id": "call_xyz789"
}File: front/api/flashcards.ts
export interface Flashcard {
id: number;
flashcard_id: string;
deck: number;
front: string;
back: string;
order: number;
created_at: string;
updated_at: string;
}
export interface Deck {
id: number;
deck_id: string;
profile: string;
chat: string | null;
name: string;
description: string;
flashcards: Flashcard[];
card_count: number;
created_at: string;
updated_at: string;
}
export interface DeckListItem {
id: number;
deck_id: string;
name: string;
description: string;
card_count: number;
created_at: string;
updated_at: string;
}
// Deck endpoints
export const fetchDecks = async (profileId: string): Promise<DeckListItem[]>
export const fetchDeck = async (deckId: string): Promise<Deck>
export const createDeck = async (name: string, description: string, profileId: string, chatId?: string): Promise<Deck>
export const updateDeck = async (deckId: string, name: string, description: string): Promise<Deck>
export const deleteDeck = async (deckId: string): Promise<void>
// Flashcard endpoints
export const fetchFlashcards = async (deckId: string): Promise<Flashcard[]>
export const createFlashcard = async (deckId: string, front: string, back: string): Promise<Flashcard>
export const updateFlashcard = async (flashcardId: string, front: string, back: string): Promise<Flashcard>
export const deleteFlashcard = async (flashcardId: string): Promise<void>File: front/app/_layout.tsx
- Replace current headerLeft (empty) with hamburger menu icon (IconSymbol "list.bullet")
- On press: toggle between "chats" and "flashcards" mode
- Show different list based on mode
File: front/app/flashcards.tsx
- FlatList of all flashcard decks
- Each item shows deck name, card count, and truncated description
- Tap deck to view/edit cards or start studying
- FAB to create new deck
File: front/app/flashcards/deck.tsx
- Header shows deck name (editable) and description
- FlatList of all flashcards in deck
- Each card shows truncated front text
- Tap card to edit
- FAB to add new card to deck
- "Study" button in header to start study mode
File: front/app/flashcards/cardEdit.tsx (or modal)
- Form with "Front" and "Back" text inputs
- Save/Cancel buttons
- Delete button if editing existing
File: front/app/flashcards/study.tsx
- Accepts deckId parameter
- Display current card (front side)
- Tap card to flip (animate)
- Previous/Next buttons to navigate
- Progress indicator (e.g., "3 / 10")
- Exit button to return to deck
File: front/app/botChat.tsx
- After sending message, check response for flashcard tool call results
- If bot created flashcards, show toast/notification: "X flashcards created"
- Store flashcard IDs in response for potential editing
┌─────────────────────────────────────────┐
│ ← My Decks │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ Biology Test Terms 12 cards │ │
│ │ Chapter 5 vocabulary │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Memory Verses 5 cards │ │
│ │ Sunday school verses │ │
│ └─────────────────────────────────────┘ │
│ │
│ [+ Create Deck]│
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ ← Back Biology Test [Study] │
├─────────────────────────────────────────┤
│ Description: Chapter 5 vocabulary │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ What is photosynthesis? [...] │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ What is cellular respiration? [...] │ │
│ └─────────────────────────────────────┘ │
│ [+ Add Card] │
└─────────────────────────────────────────┘
┌─────────────────────────────┐
│ ← Back Study (3/10) │
├─────────────────────────────┤
│ │
│ ┌───────────────────┐ │
│ │ │ │
│ │ What is the │ │
│ │ capital of │ │
│ │ France? │ │
│ │ │ │
│ │ Tap to reveal │ │
│ │ │ │
│ └───────────────────┘ │
│ │
│ ← Prev Next → │
│ │
└─────────────────────────────┘
(After tap - shows answer side)
- Icon: "list.bullet" from IconSymbol
- Position: Header left (replaces any existing back button when on root screens)
- Behavior: Opens drawer or toggles view mode
back/bots/models/flashcard.py- Deck and Flashcard modelsback/bots/serializers/flashcard_serializer.py- Serializers for both modelsback/bots/viewsets/flashcard_viewset.py- ViewSets for both models
back/bots/models/__init__.py- Export Deck, Flashcardback/bots/serializers/__init__.py- Export serializersback/server/urls.py- Add flashcard routesback/bots/models/chat.py- Handle flashcard tool calls
front/api/flashcards.ts- API module for decks and cardsfront/app/flashcards.tsx- List of flashcard decksfront/app/flashcards/deck.tsx- Deck detail with card listfront/app/flashcards/cardEdit.tsx- Create/edit card formfront/app/flashcards/study.tsx- Study mode with flip cards
front/app/_layout.tsx- Add hamburger menu, toggle between chats/flashcardsfront/app/botChat.tsx- Handle flashcard creation from bot responses
- Backend: Create models, serializers, viewset, routes
- Backend: Add tool call handler in chat response view
- Frontend: Create API module
- Frontend: Update navigation/layout with hamburger menu
- Frontend: Create flashcard decks list screen
- Frontend: Create deck detail screen with card list
- Frontend: Create card edit screen
- Frontend: Create study screen with flip animation
- Frontend: Integrate flashcard creation in chat
- Test and verify end-to-end flow