Repository: jdang00/learnterms
Pull request #28: Stage
Author: login: jdang00
Created: 2025-03-26T04:10:28Z | Merged: 2025-03-26T20:24:28Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - The admin interface now includes a searchable list and a modal dialog for streamlined question management with refined pagination.
  - The dashboard offers enhanced navigation with a dedicated sidebar, mobile-friendly menus, interactive quiz controls, and improved question navigation.
  - New components added for better user interaction, including `AddNewQuestionModal`, `PaginationControls`, `Search`, `Sidebar`, `QuestionNavigation`, `MobileMenu`, `QuizContent`, `MobileInfo`, `ActionButtons`, `AnswerOptions`, and `FlagAlert`.

- **Bug Fixes**
  - Fixed an issue with question deletion to ensure proper data handling.

- **Refactor**
  - Streamlined UI elements into modular components for a cleaner and more maintainable user experience.
  
- **UI Enhancements**
  - Improved visual structure and navigation options in the admin interface, including updated link titles with emojis and enhanced hover effects.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/src/routes/admin/+page.server.ts b/src/routes/admin/+page.server.ts
new file mode 100644
index 0000000..9983340
--- /dev/null
+++ b/src/routes/admin/+page.server.ts
@@ -0,0 +1,41 @@
+import type { PageServerLoad } from './$types';
+import { redirect } from '@sveltejs/kit';
+import { clerkClient } from 'svelte-clerk/server';
+
+export const load: PageServerLoad = async ({ locals }) => {
+	// Ensure authentication is required for this route
+	const { userId } = locals.auth;
+
+	// Early return if no user is authenticated
+	if (!userId) {
+		throw redirect(307, '/sign-in');
+	}
+
+	try {
+		// Fetch user with error handling
+		const user = await clerkClient.users.getUser(userId);
+
+		// Explicit role check with more robust validation
+		const userRole = user.publicMetadata.role;
+		if (userRole !== 'admin') {
+			throw redirect(307, '/');
+		}
+
+		// Safely serialize user data
+		return {
+			user: structuredClone(user)
+		};
+	} catch (err: unknown) {
+		// Type-safe error handling with redirects
+		if (err instanceof Error) {
+			// Log unexpected errors for server-side tracking
+			console.error('Admin route access error:', err.message);
+		} else {
+			// Handle non-Error objects
+			console.error('Unexpected error in admin route:', err);
+		}
+
+		// Redirect to home page or sign-in on any error
+		throw redirect(307, '/');
+	}
+};
diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte
index 6cb50d0..a093c3c 100644
--- a/src/routes/admin/+page.svelte
+++ b/src/routes/admin/+page.svelte
@@ -1,5 +1,8 @@
 <script>
 	import { ArrowRightIcon } from 'lucide-svelte';
+	import { useClerkContext } from 'svelte-clerk';
+
+	const ctx = useClerkContext();
 </script>
 
 <main class="max-w-5xl mx-auto px-10 mt-12">
@@ -7,29 +10,42 @@
 	<h1 class="mb-8 text-4xl font-semibold">Admin</h1>
 
 	<!-- Admin Controls as DaisyUI Cards -->
-	<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
+	<div class="grid grid-cols-1 gap-6">
 		<a
 			href="/admin/questions"
-			class="card bg-base-100 shadow-xl cursor-pointer hover:shadow-2xl transition-shadow duration-200"
-			target="_blank"
+			class="card bg-base-100 shadow-xl cursor-pointer hover:shadow-2xl transition-shadow duration-200 hover:text-primary"
 		>
 			<div class="card-body flex flex-row justify-between">
 				<div>
-					<h2 class="card-title">Manage Questions</h2>
-					<p>Create, edit, and organize quiz questions.</p>
+					<h2 class="card-title">✏️ Manage Questions</h2>
+					<p>Edit and organize quiz questions.</p>
 				</div>
 				<button class="btn btn-primary rounded-full self-center"><ArrowRightIcon /></button>
 			</div>
 		</a>
 
+		{#if ctx.user?.publicMetadata.create === 'contributor'}
+			<a
+				href="/admin/challenge"
+				class="card bg-base-100 shadow-xl cursor-pointer hover:shadow-2xl transition-shadow duration-200 hover:text-primary"
+			>
+				<div class="card-body flex flex-row justify-between">
+					<div>
+						<h2 class="card-title">🧠 Manage Challenge Questions</h2>
+						<p>Create, edit, and organize challenge questions.</p>
+					</div>
+					<button class="btn btn-primary rounded-full self-center"><ArrowRightIcon /></button>
+				</div>
+			</a>
+		{/if}
+
 		<a
 			href="/admin/progress"
-			class="card bg-base-100 shadow-xl cursor-pointer hover:shadow-2xl transition-shadow duration-200"
-			target="_blank"
+			class="card bg-base-100 shadow-xl cursor-pointer hover:shadow-2xl transition-shadow duration-200 hover:text-primary"
 		>
 			<div class="card-body flex flex-row justify-between">
 				<div>
-					<h2 class="card-title">Track User Progress</h2>
+					<h2 class="card-title">⏰ Track User Progress</h2>
 					<p>View and analyze student progress through course materials and quizzes.</p>
 				</div>
 				<button class="btn btn-primary rounded-full self-center"><ArrowRightIcon /></button>
diff --git a/src/routes/admin/challenge/+page.svelte b/src/routes/admin/challenge/+page.svelte
index 3f58345..7b3f699 100644
--- a/src/routes/admin/challenge/+page.svelte
+++ b/src/routes/admin/challenge/+page.svelte
@@ -2,9 +2,12 @@
 	import supabase from '$lib/supabaseClient';
 	import type { AdminChallengeQuestions } from '$lib/types';
 	import type { PageData } from './$types';
-	import { Trash2, Pencil, ChevronLeft, ChevronRight, Plus, Image } from 'lucide-svelte';
+	import { Trash2, Pencil, Image } from 'lucide-svelte';
 	import { browser } from '$app/environment';
 	import { createUploadThing } from '$lib/utils/uploadthing';
+	import PaginationControls from './components/PaginationControls.svelte';
+	import Search from './components/Search.svelte';
+	import AddNewQuestionModal from './components/AddNewQuestionModal.svelte';
 
 	// Retrieve the questions from props.
 	let { data }: { data: PageData } = $props();
@@ -388,7 +391,7 @@
 		isDeleting = true;
 		deleteError = null;
 
-		const { error } = await supabase.from('pharmquestions').delete().eq('id', pendingDeleteId);
+		const { error } = await supabase.from('pharmchallenge').delete().eq('id', pendingDeleteId);
 
 		isDeleting = false;
 
@@ -411,132 +414,18 @@
 		const match = option.match(/^([A-Z])\./);
 		return match ? match[1] : '';
 	}
-
-	// Function to get the full option text by letter
-	function getOptionByLetter(options: string[], letter: string): string {
-		return options.find((opt) => opt.startsWith(`${letter}.`)) || '';
-	}
 </script>
 
 <div class="flex flex-col gap-4 mt-12 mx-6">
-	<div class="flex flex-col sm:flex-row justify-between items-start">
-		<div class="flex gap-2 mb-4 sm:mb-0">
-			<button class="btn btn-primary" onclick={openAddModal}>
-				<Plus size={16} /> Add Question
-			</button>
-		</div>
-
-		<!-- Controls: Search Bar and Chapter Filter -->
-		<div class="flex flex-col sm:flex-row gap-2 sm:items-center w-full sm:w-auto">
-			<div>
-				<label class="input">
-					<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
-						<g
-							stroke-linejoin="round"
-							stroke-linecap="round"
-							stroke-width="2.5"
-							fill="none"
-							stroke="currentColor"
-						>
-							<circle cx="11" cy="11" r="8"></circle>
-							<path d="m21 21-4.3-4.3"></path>
-						</g>
-					</svg>
-					<input
-						id="search-input"
-						type="search"
-						placeholder="Search"
-						bind:value={searchQuery}
-						class="grow"
-					/>
-				</label>
-			</div>
-
-			<div>
-				<select id="chapter-select" class="select" bind:value={selectedChapter}>
-					<option value="">All Chapters</option>
-					{#each Array.from(new Set(questions.map((q) => q.chapter))).sort() as chapter}
-						{#if chapter}
-							<option value={chapter} selected={selectedChapter === chapter}>{chapter}</option>
-						{/if}
-					{/each}
-				</select>
-				<label for="chapter-select" class="sr-only">Select Chapter</label>
-			</div>
-		</div>
-	</div>
-
+	<Search bind:searchQuery bind:selectedChapter {questions} {openAddModal} />
 	<!-- Top Pagination Controls -->
-	<div class="flex flex-col sm:flex-row justify-between items-center mb-4">
-		<div class="flex items-center mb-4 sm:mb-0">
-			<span class="text-sm">
-				Showing {filteredQuestions.length ? (currentPage - 1) * questionsPerPage + 1 : 0} to
-				{Math.min(currentPage * questionsPerPage, filteredQuestions.length)} of {filteredQuestions.length}
-				entries
-			</span>
-			<div class="ml-4">
-				<label for="top-per-page" class="sr-only">Items per page</label>
-				<select id="top-per-page" class="select select-sm" bind:value={questionsPerPage}>
-					<option value={5}>5 per page</option>
-					<option value={10}>10 per page</option>
-					<option value={25}>25 per page</option>
-					<option value={50}>50 per page</option>
-					<option value={100}>100 per page</option>
-				</select>
-			</div>
-		</div>
-
-		<div class="join">
-			<button class="join-item btn btn-sm" disabled={currentPage === 1} onclick={() => goToPage(1)}>
-				First
-			</button>
-			<button
-				class="join-item btn btn-sm"
-				disabled={currentPage === 1}
-				onclick={() => goToPage(currentPage - 1)}
-			>
-				<ChevronLeft size={16} />
-			</button>
-
-			{#each Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
-				// Calculate which page numbers to show (center around current page)
-				let pageNum;
-				if (totalPages <= 5) {
-					pageNum = i + 1;
-				} else if (currentPage <= 3) {
-					pageNum = i + 1;
-				} else if (currentPage >= totalPages - 2) {
-					pageNum = totalPages - 4 + i;
-				} else {
-					pageNum = currentPage - 2 + i;
-				}
-				return pageNum;
-			}) as pageNum}
-				<button
-					class="join-item btn btn-sm"
-					class:btn-active={pageNum === currentPage}
-					onclick={() => goToPage(pageNum)}
-				>
-					{pageNum}
-				</button>
-			{/each}
-
-			<button
-				class="join-item btn btn-sm"
-				disabled={currentPage === totalPages}
-				onclick={() => goToPage(currentPage + 1)}
-			>
-				<ChevronRight size={16} />
-			</button>
-			<button
-				class="join-item btn btn-sm"
-				disabled={currentPage === totalPages}
-				onclick={() => goToPage(totalPages)}
-			>
-				Last
-			</button>
-		</div>
-	</div>
+	<PaginationControls
+		{currentPage}
+		{totalPages}
+		{questionsPerPage}
+		totalEntries={filteredQuestions.length}
+		{goToPage}
+	/>
 </div>
 
 <!-- Delete Confirmation Modal -->
@@ -604,224 +493,22 @@
 </div>
 
 <!-- Add New Question Modal -->
-<div>
-	<dialog class="modal max-w-full p-4" class:modal-open={isAddModalOpen}>
-		<div class="modal-box w-11/12 max-w-4xl">
-			<form method="dialog">
-				<button
-					class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
-					onclick={() => {
-						isAddModalOpen = false;
-						newQuestion = null;
-					}}>✕</button
-				>
-			</form>
-			<h3 class="text-lg font-bold mb-4">Add New Question</h3>
 
-			{#if newQuestion}
-				<div class="grid grid-cols-1 gap-6">
-					<!-- Chapter -->
-					<div class="form-control">
-						<label for="new-chapter" class="label">
-							<span class="label-text">Chapter</span>
-						</label>
-						<select
-							id="new-chapter"
-							class="select select-bordered"
-							bind:value={newQuestion.chapter}
-						>
-							{#each Array.from(new Set(questions.map((q) => q.chapter))).sort() as chapter}
-								{#if chapter}
-									<option value={chapter}>{chapter}</option>
-								{/if}
-							{/each}
-						</select>
-					</div>
-
-					<!-- Question Text -->
-					<div class="form-control">
-						<label for="new-question-text" class="label">
-							<span class="label-text">Question</span>
-						</label>
-						<textarea
-							id="new-question-text"
-							class="textarea textarea-bordered h-24"
-							bind:value={newQuestion.question_data.question}
-							placeholder="Enter question text"
-						></textarea>
-					</div>
-
-					<!-- Options -->
-					<fieldset class="form-control">
-						<legend class="text-base font-medium mb-2">Options</legend>
-
-						{#each newQuestion.question_data.options as _, i}
-							<div class="flex items-center mb-2">
-								<label for={`new-option-${i}`} class="sr-only">Option {i + 1}</label>
-								<input
-									id={`new-option-${i}`}
-									type="text"
-									class="input input-bordered w-full mr-2"
-									bind:value={newQuestion.question_data.options[i]}
-									placeholder={`Option ${String.fromCharCode(65 + i)}`}
-								/>
-								<button
-									class="btn btn-sm btn-error btn-soft"
-									onclick={() => removeNewOption(i)}
-									type="button"
-									aria-label={`Remove option ${i + 1}`}
-									disabled={newQuestion.question_data.options.length <= 2}
-								>
-									<Trash2 size="16" />
-								</button>
-							</div>
-						{/each}
-
-						<button class="btn btn-sm btn-outline mt-2 w-full" onclick={addNewOption} type="button">
-							Add Option
-						</button>
-					</fieldset>
-
-					<!-- Correct Answers -->
-					<fieldset class="form-control">
-						<legend class="text-base font-medium mb-2">Correct Answers</legend>
-
-						{#each newQuestion.question_data.correct_answers as _, i}
-							<div class="flex items-center mb-2">
-								<label for={`new-correct-answer-${i}`} class="sr-only">Correct answer {i + 1}</label
-								>
-								<select
-									id={`new-correct-answer-${i}`}
-									class="select select-bordered w-full mr-2"
-									bind:value={newQuestion.question_data.correct_answers[i]}
-								>
-									<option value="" disabled>Select correct answer</option>
-									{#each ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'] as letter, letterIdx}
-										<!-- Only include options that exist -->
-										{#if letterIdx < newQuestion.question_data.options.length}
-											<option value={letter}>{letter}</option>
-										{/if}
-									{/each}
-								</select>
-								<button
-									class="btn btn-sm btn-error btn-soft"
-									onclick={() => removeNewCorrectAnswer(i)}
-									type="button"
-									aria-label={`Remove correct answer ${i + 1}`}
-									disabled={newQuestion.question_data.correct_answers.length <= 1}
-								>
-									<Trash2 size="16" />
-								</button>
-							</div>
-						{/each}
-
-						<button
-							class="btn btn-sm btn-outline mt-2 w-full"
-							onclick={addNewCorrectAnswer}
-							type="button"
-						>
-							Add Correct Answer
-						</button>
-					</fieldset>
-
-					<!-- Explanation -->
-					<div class="form-control">
-						<label for="new-explanation" class="label">
-							<span class="label-text">Explanation</span>
-						</label>
-						<textarea
-							id="new-explanation"
-							class="textarea textarea-bordered h-24"
-							bind:value={newQuestion.question_data.explanation}
-							placeholder="Enter explanation text"
-						></textarea>
-					</div>
-
-					<!-- Image Upload -->
-					<fieldset class="form-control">
-						<legend class="text-base font-medium mb-2">Upload Picture</legend>
-
-						{#if newImageUrl}
-							<div class="mb-4">
-								<div class="flex items-center mb-2">
-									<img src={newImageUrl} alt="Uploaded preview" class="max-h-56 rounded-lg" />
-									<button
-										class="btn btn-sm btn-error ml-4"
-										onclick={() => (newImageUrl = null)}
-										type="button"
-									>
-										Remove Image
-									</button>
-								</div>
-							</div>
-						{/if}
-
-						<div class="flex items-center">
-							<input
-								type="file"
-								accept="image/*"
-								onchange={handleNewImageUpload}
-								class="file-input file-input-bordered w-full"
-								disabled={addImageUploading}
-							/>
-							{#if addImageUploading}
-								<div class="ml-2">
-									<span class="loading loading-spinner text-primary"></span>
-								</div>
-							{/if}
-						</div>
-					</fieldset>
-				</div>
-
-				{#if addError}
-					<div class="alert alert-error my-4">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							class="stroke-current shrink-0 h-6 w-6"
-							fill="none"
-							viewBox="0 0 24 24"
-						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								stroke-width="2"
-								d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
-							/>
-						</svg>
-						<span>Error: {addError}</span>
-					</div>
-				{/if}
-
-				<div class="modal-action">
-					<button
-						class="btn btn-outline"
-						onclick={() => {
-							isAddModalOpen = false;
-							newQuestion = null;
-						}}
-						disabled={isAddingSaving || addImageUploading}
-						type="button"
-					>
-						Cancel
-					</button>
-					<button
-						class="btn btn-primary"
-						onclick={addQuestion}
-						disabled={isAddingSaving || addImageUploading}
-						type="button"
-					>
-						{#if isAddingSaving}
-							<span class="loading loading-spinner"></span>
-							Adding...
-						{:else}
-							Add Question
-						{/if}
-					</button>
-				</div>
-			{/if}
-		</div>
-	</dialog>
-</div>
+<AddNewQuestionModal
+	bind:isAddModalOpen
+	bind:newQuestion
+	bind:newImageUrl
+	{questions}
+	{removeNewOption}
+	{addNewOption}
+	{removeNewCorrectAnswer}
+	{addNewCorrectAnswer}
+	{handleNewImageUpload}
+	{addImageUploading}
+	{addError}
+	{isAddingSaving}
+	{addQuestion}
+/>
 
 <!-- Edit Question Modal -->
 <div>
@@ -1044,7 +731,7 @@
 </div>
 
 <!-- Questions Table -->
-<div class="overflow-x-auto rounded-box border border-base-content/12 bg-base-100 mx-6">
+<div class="overflow-x-auto rounded-box border border-base-content/12 bg-base-100 mx-6 my-4">
 	<table class="table">
 		<thead>
 			<tr>
@@ -1113,73 +800,10 @@
 </div>
 
 <!-- Bottom Pagination Controls -->
-<div class="flex flex-col sm:flex-row justify-between items-center px-6 py-4">
-	<div class="flex items-center mb-4 sm:mb-0">
-		<span class="text-sm">
-			Showing {filteredQuestions.length ? (currentPage - 1) * questionsPerPage + 1 : 0} to
-			{Math.min(currentPage * questionsPerPage, filteredQuestions.length)} of {filteredQuestions.length}
-			entries
-		</span>
-		<div class="ml-4">
-			<label for="bottom-per-page" class="sr-only">Items per page</label>
-			<select id="bottom-per-page" class="select select-sm" bind:value={questionsPerPage}>
-				<option value={5}>5 per page</option>
-				<option value={10}>10 per page</option>
-				<option value={25}>25 per page</option>
-				<option value={50}>50 per page</option>
-				<option value={100}>100 per page</option>
-			</select>
-		</div>
-	</div>
-
-	<div class="join">
-		<button class="join-item btn btn-sm" disabled={currentPage === 1} onclick={() => goToPage(1)}>
-			First
-		</button>
-		<button
-			class="join-item btn btn-sm"
-			disabled={currentPage === 1}
-			onclick={() => goToPage(currentPage - 1)}
-		>
-			<ChevronLeft size={16} />
-		</button>
-
-		{#each Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
-			// Calculate which page numbers to show (center around current page)
-			let pageNum;
-			if (totalPages <= 5) {
-				pageNum = i + 1;
-			} else if (currentPage <= 3) {
-				pageNum = i + 1;
-			} else if (currentPage >= totalPages - 2) {
-				pageNum = totalPages - 4 + i;
-			} else {
-				pageNum = currentPage - 2 + i;
-			}
-			return pageNum;
-		}) as pageNum}
-			<button
-				class="join-item btn btn-sm"
-				class:btn-active={pageNum === currentPage}
-				onclick={() => goToPage(pageNum)}
-			>
-				{pageNum}
-			</button>
-		{/each}
-
-		<button
-			class="join-item btn btn-sm"
-			disabled={currentPage === totalPages}
-			onclick={() => goToPage(currentPage + 1)}
-		>
-			<ChevronRight size={16} />
-		</button>
-		<button
-			class="join-item btn btn-sm"
-			disabled={currentPage === totalPages}
-			onclick={() => goToPage(totalPages)}
-		>
-			Last
-		</button>
-	</div>
-</div>
+<PaginationControls
+	{currentPage}
+	{totalPages}
+	{questionsPerPage}
+	totalEntries={filteredQuestions.length}
+	{goToPage}
+/>
diff --git a/src/routes/admin/challenge/components/AddNewQuestionModal.svelte b/src/routes/admin/challenge/components/AddNewQuestionModal.svelte
new file mode 100644
index 0000000..63019f5
--- /dev/null
+++ b/src/routes/admin/challenge/components/AddNewQuestionModal.svelte
@@ -0,0 +1,237 @@
+<script>
+	import { Trash2 } from 'lucide-svelte';
+	let {
+		isAddModalOpen = $bindable(),
+		newQuestion = $bindable(),
+		questions,
+		removeNewOption,
+		addNewOption,
+		removeNewCorrectAnswer,
+		addNewCorrectAnswer,
+		newImageUrl = $bindable(),
+		handleNewImageUpload,
+		addImageUploading,
+		addError,
+		isAddingSaving,
+		addQuestion
+	} = $props();
+</script>
+
+<div>
+	<dialog class="modal max-w-full p-4" class:modal-open={isAddModalOpen}>
+		<div class="modal-box w-11/12 max-w-4xl">
+			<form method="dialog">
+				<button
+					class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
+					onclick={() => {
+						isAddModalOpen = false;
+						newQuestion = null;
+					}}>✕</button
+				>
+			</form>
+			<h3 class="text-lg font-bold mb-4">Add New Question</h3>
+
+			{#if newQuestion}
+				<div class="grid grid-cols-1 gap-6">
+					<!-- Chapter -->
+					<div class="form-control">
+						<label for="new-chapter" class="label">
+							<span class="label-text">Chapter</span>
+						</label>
+						<select
+							id="new-chapter"
+							class="select select-bordered"
+							bind:value={newQuestion.chapter}
+						>
+							{#each Array.from(new Set(questions.map((q) => q.chapter))).sort() as chapter}
+								{#if chapter}
+									<option value={chapter}>{chapter}</option>
+								{/if}
+							{/each}
+						</select>
+					</div>
+
+					<!-- Question Text -->
+					<div class="form-control">
+						<label for="new-question-text" class="label">
+							<span class="label-text">Question</span>
+						</label>
+						<textarea
+							id="new-question-text"
+							class="textarea textarea-bordered h-24"
+							bind:value={newQuestion.question_data.question}
+							placeholder="Enter question text"
+						></textarea>
+					</div>
+
+					<!-- Options -->
+					<fieldset class="form-control">
+						<legend class="text-base font-medium mb-2">Options</legend>
+
+						{#each newQuestion.question_data.options as _, i}
+							<div class="flex items-center mb-2">
+								<label for={`new-option-${i}`} class="sr-only">Option {i + 1}</label>
+								<input
+									id={`new-option-${i}`}
+									type="text"
+									class="input input-bordered w-full mr-2"
+									bind:value={newQuestion.question_data.options[i]}
+									placeholder={`Option ${String.fromCharCode(65 + i)}`}
+								/>
+								<button
+									class="btn btn-sm btn-error btn-soft"
+									onclick={() => removeNewOption(i)}
+									type="button"
+									aria-label={`Remove option ${i + 1}`}
+									disabled={newQuestion.question_data.options.length <= 2}
+								>
+									<Trash2 size="16" />
+								</button>
+							</div>
+						{/each}
+
+						<button class="btn btn-sm btn-outline mt-2 w-full" onclick={addNewOption} type="button">
+							Add Option
+						</button>
+					</fieldset>
+
+					<!-- Correct Answers -->
+					<fieldset class="form-control">
+						<legend class="text-base font-medium mb-2">Correct Answers</legend>
+
+						{#each newQuestion.question_data.correct_answers as _, i}
+							<div class="flex items-center mb-2">
+								<label for={`new-correct-answer-${i}`} class="sr-only">Correct answer {i + 1}</label
+								>
+								<select
+									id={`new-correct-answer-${i}`}
+									class="select select-bordered w-full mr-2"
+									bind:value={newQuestion.question_data.correct_answers[i]}
+								>
+									<option value="" disabled>Select correct answer</option>
+									{#each ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'] as letter, letterIdx}
+										<!-- Only include options that exist -->
+										{#if letterIdx < newQuestion.question_data.options.length}
+											<option value={letter}>{letter}</option>
+										{/if}
+									{/each}
+								</select>
+								<button
+									class="btn btn-sm btn-error btn-soft"
+									onclick={() => removeNewCorrectAnswer(i)}
+									type="button"
+									aria-label={`Remove correct answer ${i + 1}`}
+									disabled={newQuestion.question_data.correct_answers.length <= 1}
+								>
+									<Trash2 size="16" />
+								</button>
+							</div>
+						{/each}
+
+						<button
+							class="btn btn-sm btn-outline mt-2 w-full"
+							onclick={addNewCorrectAnswer}
+							type="button"
+						>
+							Add Correct Answer
+						</button>
+					</fieldset>
+
+					<!-- Explanation -->
+					<div class="form-control">
+						<label for="new-explanation" class="label">
+							<span class="label-text">Explanation</span>
+						</label>
+						<textarea
+							id="new-explanation"
+							class="textarea textarea-bordered h-24"
+							bind:value={newQuestion.question_data.explanation}
+							placeholder="Enter explanation text"
+						></textarea>
+					</div>
+
+					<!-- Image Upload -->
+					<fieldset class="form-control">
+						<legend class="text-base font-medium mb-2">Upload Picture</legend>
+
+						{#if newImageUrl}
+							<div class="mb-4">
+								<div class="flex items-center mb-2">
+									<img src={newImageUrl} alt="Uploaded preview" class="max-h-56 rounded-lg" />
+									<button
+										class="btn btn-sm btn-error ml-4"
+										onclick={() => (newImageUrl = null)}
+										type="button"
+									>
+										Remove Image
+									</button>
+								</div>
+							</div>
+						{/if}
+
+						<div class="flex items-center">
+							<input
+								type="file"
+								accept="image/*"
+								onchange={handleNewImageUpload}
+								class="file-input file-input-bordered w-full"
+								disabled={addImageUploading}
+							/>
+							{#if addImageUploading}
+								<div class="ml-2">
+									<span class="loading loading-spinner text-primary"></span>
+								</div>
+							{/if}
+						</div>
+					</fieldset>
+				</div>
+
+				{#if addError}
+					<div class="alert alert-error my-4">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							class="stroke-current shrink-0 h-6 w-6"
+							fill="none"
+							viewBox="0 0 24 24"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								stroke-width="2"
+								d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
+							/>
+						</svg>
+						<span>Error: {addError}</span>
+					</div>
+				{/if}
+
+				<div class="modal-action">
+					<button
+						class="btn btn-outline"
+						onclick={() => {
+							isAddModalOpen = false;
+							newQuestion = null;
+						}}
+						disabled={isAddingSaving || addImageUploading}
+						type="button"
+					>
+						Cancel
+					</button>
+					<button
+						class="btn btn-primary"
+						onclick={addQuestion}
+						disabled={isAddingSaving || addImageUploading}
+						type="button"
+					>
+						{#if isAddingSaving}
+							<span class="loading loading-spinner"></span>
+							Adding...
+						{:else}
+							Add Question
+						{/if}
+					</button>
+				</div>
+			{/if}
+		</div>
+	</dialog>
+</div>
diff --git a/src/routes/admin/challenge/components/PaginationControls.svelte b/src/routes/admin/challenge/components/PaginationControls.svelte
new file mode 100644
index 0000000..93ad649
--- /dev/null
+++ b/src/routes/admin/challenge/components/PaginationControls.svelte
@@ -0,0 +1,78 @@
+<script lang="ts">
+	import { ChevronLeft, ChevronRight } from 'lucide-svelte';
+
+	let { currentPage, totalPages, questionsPerPage, totalEntries, goToPage } = $props();
+</script>
+
+<div class="flex flex-col sm:flex-row justify-between items-center">
+	<!-- Left Side: Showing entries and items per page -->
+	<div class="flex items-center">
+		<span class="text-sm">
+			Showing {totalEntries ? (currentPage - 1) * questionsPerPage + 1 : 0} to {Math.min(
+				currentPage * questionsPerPage,
+				totalEntries
+			)} of {totalEntries} entries
+		</span>
+		<div class="ml-4">
+			<label for="per-page" class="sr-only">Items per page</label>
+			<select id="per-page" class="select select-sm" bind:value={questionsPerPage}>
+				<option value={5}>5 per page</option>
+				<option value={10}>10 per page</option>
+				<option value={25}>25 per page</option>
+				<option value={50}>50 per page</option>
+				<option value={100}>100 per page</option>
+			</select>
+		</div>
+	</div>
+
+	<!-- Right Side: Navigation buttons -->
+	<div class="join">
+		<button class="join-item btn btn-sm" disabled={currentPage === 1} onclick={() => goToPage(1)}>
+			First
+		</button>
+		<button
+			class="join-item btn btn-sm"
+			disabled={currentPage === 1}
+			onclick={() => goToPage(currentPage - 1)}
+		>
+			<ChevronLeft size={16} />
+		</button>
+
+		{#each Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
+			let pageNum;
+			if (totalPages <= 5) {
+				pageNum = i + 1;
+			} else if (currentPage <= 3) {
+				pageNum = i + 1;
+			} else if (currentPage >= totalPages - 2) {
+				pageNum = totalPages - 4 + i;
+			} else {
+				pageNum = currentPage - 2 + i;
+			}
+			return pageNum;
+		}) as pageNum}
+			<button
+				class="join-item btn btn-sm"
+				class:btn-active={pageNum === currentPage}
+				onclick={() => goToPage(pageNum)}
+			>
+				{pageNum}
+			</button>
+		{/each}
+
+		<button
+			class="join-item btn btn-sm"
+			disabled={currentPage === totalPages}
+			onclick={() => goToPage(currentPage + 1)}
+		>
+			<ChevronRight size={16} />
+		</button>
+		<button
+			class="join-item btn btn-sm"
+			disabled={currentPage === totalPages}
+			onclick={() => goToPage(totalPages)}
+		>
+			Last
+		</button>
+	</div>
+</div>
diff --git a/src/routes/admin/challenge/components/Search.svelte b/src/routes/admin/challenge/components/Search.svelte
new file mode 100644
index 0000000..c517d8e
--- /dev/null
+++ b/src/routes/admin/challenge/components/Search.svelte
@@ -0,0 +1,59 @@
+<script>
+	import { Plus, ArrowLeft } from 'lucide-svelte';
+	let {
+		searchQuery = $bindable(),
+		questions = $bindable(),
+		selectedChapter = $bindable(),
+		openAddModal
+	} = $props();
+</script>
+
+<div class="flex flex-col sm:flex-row justify-between items-start">
+	<div class="flex flex-row gap-4">
+		<a href="/admin" class="btn btn-ghost"><ArrowLeft /></a>
+		<div class="flex gap-2 mb-4 sm:mb-0">
+			<button class="btn btn-primary" onclick={openAddModal}>
+				<Plus size={16} /> Add Question
+			</button>
+		</div>
+	</div>
+
+	<!-- Controls: Search Bar and Chapter Filter -->
+	<div class="flex flex-col sm:flex-row gap-2 sm:items-center w-full sm:w-auto">
+		<div>
+			<label class="input">
+				<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+					<g
+						stroke-linejoin="round"
+						stroke-linecap="round"
+						stroke-width="2.5"
+						fill="none"
+						stroke="currentColor"
+					>
+						<circle cx="11" cy="11" r="8"></circle>
+						<path d="m21 21-4.3-4.3"></path>
+					</g>
+				</svg>
+				<input
+					id="search-input"
+					type="search"
+					placeholder="Search"
+					bind:value={searchQuery}
+					class="grow"
+				/>
+			</label>
+		</div>
+
+		<div>
+			<select id="chapter-select" class="select" bind:value={selectedChapter}>
+				<option value="">All Chapters</option>
+				{#each Array.from(new Set(questions.map((q) => q.chapter))).sort() as chapter}
+					{#if chapter}
+						<option value={chapter}>{chapter}</option>
+					{/if}
+				{/each}
+			</select>
+			<label for="chapter-select" class="sr-only">Select Chapter</label>
+		</div>
+	</div>
+</div>
diff --git a/src/routes/admin/progress/+page.svelte b/src/routes/admin/progress/+page.svelte
index e680719..169ba3f 100644
--- a/src/routes/admin/progress/+page.svelte
+++ b/src/routes/admin/progress/+page.svelte
@@ -1,7 +1,7 @@
 <script lang="ts">
 	import type { PageData } from './$types';
 	import type { RawUserProgress, UserProgress } from '$lib/types';
-	import { Search } from 'lucide-svelte';
+	import { Search, ArrowLeft } from 'lucide-svelte';
 
 	// Props and data
 	let { data }: { data: PageData } = $props();
@@ -162,7 +162,11 @@
 		<label for="my-drawer" class="drawer-overlay"></label>
 		<div class="menu p-4 w-80 h-full bg-base-100 shadow-lg">
 			<div class="flex flex-col gap-4">
-				<h2 class="text-xl font-bold px-1 pt-2">Users</h2>
+				<div class="flex flex-row gap-2">
+					<a href="/admin" class="btn btn-ghost"><ArrowLeft /></a>
+
+					<h2 class="text-xl font-bold px-1 pt-2">Users</h2>
+				</div>
 
 				<div class="relative">
 					<input
diff --git a/src/routes/admin/questions/+page.svelte b/src/routes/admin/questions/+page.svelte
index 5fa28df..c7d49f0 100644
--- a/src/routes/admin/questions/+page.svelte
+++ b/src/routes/admin/questions/+page.svelte
@@ -2,7 +2,7 @@
 	import supabase from '$lib/supabaseClient';
 	import type { AdminQuestions } from '$lib/types';
 	import type { PageData } from './$types';
-	import { Trash2, Pencil, ChevronLeft, ChevronRight } from 'lucide-svelte';
+	import { Trash2, Pencil, ChevronLeft, ChevronRight, ArrowLeft } from 'lucide-svelte';
 	import { browser } from '$app/environment';
 
 	// Retrieve the questions from props.
@@ -235,9 +235,15 @@
 
 <div class="flex flex-col gap-4 mt-12 mx-6">
 	<div class="flex flex-col sm:flex-row justify-between items-start">
-		<button class="btn btn-error btn-soft mb-4 sm:mb-0" onclick={() => (deleteAllModal = true)}
-			>Delete All</button
-		>
+		<div class="flex flex-row gap-2">
+			<a href="/admin" class="btn btn-ghost"><ArrowLeft /></a>
+
+			<button class="btn btn-error btn-soft mb-4 sm:mb-0" onclick={() => (deleteAllModal = true)}
+				>Delete All</button
+			>
+		</div>
+
+		<h2 class="text-xl font-bold px-1 pt-2">Questions</h2>
 
 		<!-- Controls: Search Bar and Chapter Filter -->
 		<div class="flex flex-col sm:flex-row gap-2 sm:items-center w-full sm:w-auto">
diff --git a/src/routes/dashboard/pharm/[slug]/+page.svelte b/src/routes/dashboard/pharm/[slug]/+page.svelte
index f99b883..fe6063d 100644
--- a/src/routes/dashboard/pharm/[slug]/+page.svelte
+++ b/src/routes/dashboard/pharm/[slug]/+page.svelte
@@ -1,20 +1,13 @@
 <script lang="ts">
 	import type { PageData } from './$types';
 	import { onDestroy, tick } from 'svelte';
-	import {
-		ArrowLeft,
-		Eye,
-		Flag,
-		ArrowRight,
-		Shuffle,
-		BookmarkCheck,
-		ChevronUp,
-		ListRestart,
-		ArrowDownNarrowWide,
-		Lightbulb
-	} from 'lucide-svelte';
 	import { QuestionMap } from './states.svelte';
 	import { useClerkContext } from 'svelte-clerk';
+	import Sidebar from './components/sidebar/Sidebar.svelte';
+	import QuestionNavigation from './components/quiz/QuestionNavigation.svelte';
+	import MobileMenu from './components/mobile/MobileMenu.svelte';
+	import QuizContent from './components/quiz/QuizContent.svelte';
+	import MobileInfo from './components/mobile/MobileInfo.svelte';
 
 	const ctx = useClerkContext();
 	const admin = $derived(ctx.user?.publicMetadata.role === 'admin');
@@ -133,385 +126,14 @@
 </script>
 
 <div class="flex flex-row max-h-screen lg:h-screen lg:border-t border-b border-base-300">
-	<!--Sidebar -->
-	<div class="hidden lg:block w-1/4 lg:border-r border-base-300 overflow-y-auto">
-		<div class="flex flex-row justify-between">
-			<a class="btn btn-ghost mt-4 ms-2" href="/dashboard"> <ArrowLeft />Back</a>
-		</div>
-
-		<div class="mx-8 mt-4">
-			<p class="font-bold text-sm tracking-wide text-secondary mb-2">
-				CHAPTER {qm.chapterData.chapter}
-			</p>
-			<h1 class="text-3xl font-bold">{qm.chapterData.name}</h1>
-			<p class="text-base-content mt-2">{qm.chapterData.desc}</p>
-
-			<p class="text-base-content/60 mt-4">{qm.progress}% done.</p>
-			<p>
-				<progress
-					class="progress progress-success w-full transition-colors"
-					value={qm.progress}
-					max="100"
-				></progress>
-			</p>
-
-			<div class="flex flex-col justify-center">
-				<div class="card bg-base-100 shadow-xl mt-12">
-					<div class="card-body">
-						<div class="flex flex-row flex-wrap justify-between border-b pb-2">
-							<h2 class="card-title">Solution</h2>
-							<div class="flex flex-row">
-								<kbd class="kbd kbd-sm hidden xl:block self-center me-1">tab</kbd>
-
-								<button class="btn btn-ghost" onclick={() => qm.handleSolution()}><Eye /></button>
-							</div>
-						</div>
-
-						<p
-							class={`mt-2 transition-all duration-300 ${qm.showSolution ? 'blur-none' : 'blur-sm'}`}
-						>
-							{qm.questionSolution}
-						</p>
-					</div>
-				</div>
-
-				<div class="flex flex-row mt-12 justify-center space-x-2">
-					<button class="btn btn-error btn-soft" onclick={() => (qm.isResetModalOpen = true)}
-						>Reset</button
-					>
-
-					<div class="tooltip" data-tip="Shortcuts">
-						<div class="tooltip-content text-black font-mono flex flex-col gap-1 p-2">
-							<div class="flex items-center gap-8 justify-between">
-								<kbd class="kbd">shift + s</kbd>
-								<span class="text-white">Shuffle</span>
-							</div>
-							<div class="flex items-center gap-8 justify-between">
-								<kbd class="kbd">f</kbd>
-								<span class="text-white">Flag</span>
-							</div>
-							<div class="flex items-center gap-8 justify-between">
-								<kbd class="kbd">enter</kbd>
-								<span class="text-white">Check</span>
-							</div>
-							<div class="flex flex-col gap-1">
-								<div class="flex items-center gap-8 justify-between">
-									<kbd class="kbd">arrows</kbd>
-									<span class="text-white">Navigation</span>
-								</div>
-							</div>
-							<div class="flex items-center gap-8 justify-between">
-								<kbd class="kbd">esc</kbd>
-								<span class="text-white">Clear</span>
-							</div>
-						</div>
-						<button class="btn btn-ghost">
-							<Lightbulb />
-						</button>
-					</div>
-					{#if admin}
-						<a class="btn btn-info btn-soft" href="/admin" target="_blank">Admin</a>
-					{/if}
-				</div>
-			</div>
-		</div>
-	</div>
-
-	<div>
-		<dialog class="modal max-w-full p-4" class:modal-open={qm.isResetModalOpen}>
-			<div class="modal-box">
-				<form method="dialog">
-					<button
-						class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
-						onclick={() => (qm.isResetModalOpen = false)}>✕</button
-					>
-				</form>
-				<h3 class="text-lg font-bold">Reset Progress</h3>
-				<p class="py-4">
-					Do you want to start over? All current progress for this chapter will be lost.
-				</p>
-
-				<div class="flex justify-end space-x-2">
-					<button class="btn btn-outline" onclick={() => (qm.isResetModalOpen = false)}
-						>Cancel</button
-					>
-					<button class="btn btn-error" onclick={() => qm.reset()}>Reset</button>
-				</div>
-			</div>
-		</dialog>
-	</div>
-
-	<!--Mobile info -->
+	<Sidebar {qm} {admin} />
+	<MobileInfo {qm} />
 	<div class="container mx-auto lg:w-3/4 flex flex-col items-center lg:min-h-screen">
-		<div
-			class="lg:hidden flex flex-row mt-2 items-center w-full justify-between sticky top-0 bg-base-100 z-40 py-2"
-		>
-			<a class="btn btn-ghost flex-shrink-0 flex items-center ms-3" href="/dashboard">
-				<ArrowLeft />
-			</a>
-
-			<div class="flex flex-row gap-2 justify-center grow">
-				<p class="font-bold tracking-wide text-secondary hidden md:block">
-					CHAPTER {qm.chapterData.chapter}
-				</p>
-				<p class="hidden md:block">·</p>
-				<h1 class="font-bold">{qm.chapterData.name}</h1>
-			</div>
-
-			<div class="flex-shrink-0 w-24"></div>
-		</div>
-
-		{#if qm.fm.noFlags}
-			<!-- Alert Box (Responsive & Centered) -->
-			<div
-				role="alert"
-				class="alert alert-warning fixed top-4 sm:top-6 md:top-8 left-1/2 transform -translate-x-1/2 z-50 w-11/12 sm:w-max max-w-xs sm:max-w-sm md:max-w-md p-4 text-center animate-fade"
-			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					class="h-6 w-6 shrink-0 stroke-current inline-block align-middle mr-2"
-					fill="none"
-					viewBox="0 0 24 24"
-				>
-					<path
-						stroke-linecap="round"
-						stroke-linejoin="round"
-						stroke-width="2"
-						d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
-					/>
-				</svg>
-				<span class="align-middle">No Questions Flagged.</span>
-				<button
-					class="btn btn-sm btn-ghost btn-warning"
-					onclick={() => {
-						qm.fm.noFlags = false;
-					}}>X</button
-				>
-			</div>
-		{/if}
-
-		<!-- Question Selection Menu -->
-		<div class="flex flex-row w-full mb-4 overflow-x-auto lg:mb-0 lg:mt-6 space-x-2 relative">
-			{#key qm.fm.flagCount}
-				{#each qm.getCurrentQuestionIds() as id, index}
-					<div class="indicator">
-						{#if qm.fm.flags.has(id)}
-							<span
-								class="indicator-item indicator-start badge badge-warning badge-xs !right-10 translate-x-1/4 translate-y-1/4"
-							></span>
-						{/if}
-						<button
-							class="btn btn-circle btn-soft mx-2 {qm.currentlySelectedId === id
-								? 'btn-primary'
-								: 'btn-outline'} {qm.selectedAnswers[id]?.selected?.size > 0 ? 'btn-accent' : ''}"
-							aria-label="question {index + 1}"
-							onclick={() => qm.changeSelected(id)}
-							bind:this={qm.questionButtons[index]}
-						>
-							{index + 1}
-						</button>
-					</div>
-				{/each}
-			{/key}
-		</div>
-
+		<QuestionNavigation {qm} />
 		<div class="hidden sm:block border-t border-base-300 w-full my-6"></div>
-
-		<!--Quizzing Sections -->
-		{#if qm.questionMap[qm.currentlySelectedId]}
-			<div class="w-full mb-8 mt-2 overflow-y-auto max-h-[70vh] pb-16 sm:pb-0">
-				<div class="mx-4 sm:mx-6">
-					<div class="flex flex-row justify-between">
-						<div class="font-bold text-lg sm:text-xl mb-4 self-center">
-							{qm.questionMap[qm.currentlySelectedId].question_data.question}
-							<span class="text-base-content/50 font-medium text-sm ms-2"
-								>Pick {qm.correctAnswersCount}.</span
-							>
-						</div>
-
-						<div class="dropdown dropdown-end lg:block hidden">
-							<div tabindex="0" role="button" class="btn btn-soft btn-accent m-1 btn-circle">
-								<ArrowDownNarrowWide />
-							</div>
-							<ul
-								tabindex="-1"
-								class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
-							>
-								<li>
-									<button onclick={qm.toggleSortByFlagged}
-										><Flag size="16" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
-									>
-								</li>
-								<li>
-									<button onclick={qm.toggleShowIncomplete}>
-										<BookmarkCheck size="16" />
-										{qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
-									</button>
-								</li>
-							</ul>
-						</div>
-					</div>
-
-					{#key qm.refreshKey}
-						<div class="flex flex-col justify-start mt-4 space-y-4">
-							{#each qm.questionAnswerStates as option, index}
-								<label
-									class="label cursor-pointer rounded-full flex items-center border-2 border-base-300 bg-base-200 transition-colors
-        {qm.showSolution ? (option.isCorrect ? 'border-success' : 'border-error') : ''}"
-								>
-									<input
-										type="checkbox"
-										class="checkbox checkbox-primary checkbox-sm ms-6"
-										checked={option.isSelected ? true : false}
-										onclick={() => qm.toggleOption(index)}
-										disabled={option.isEliminated || qm.showSolution}
-									/>
-									<span
-										class="flex-grow text-wrap break-words ml-8 my-4 {option.isEliminated
-											? 'line-through opacity-50'
-											: ''}"
-									>
-										{option.text}
-									</span>
-									<div class="flex items-center justify-center w-16 mr-4">
-										<button
-											class="btn btn-ghost btn-circle"
-											onclick={() => qm.toggleElimination(index)}
-											disabled={qm.showSolution}
-											aria-label="eliminate option {option.letter}"
-										>
-											<Eye />
-										</button>
-									</div>
-								</label>
-							{/each}
-						</div>
-					{/key}
-				</div>
-
-				<!--Button Section -->
-				<div class=" flex-row justify-center mt-8 gap-4 hidden lg:flex">
-					<button class="btn btn-outline" onclick={qm.clearSelectedAnswers}>Clear</button>
-					<button class="btn btn-soft btn-success" onclick={qm.checkAnswers}>Check</button>
-					<button
-						class="btn btn-warning btn-soft"
-						aria-label="flag question {qm.questionIds.indexOf(qm.currentlySelectedId)}"
-						onclick={() => qm.toggleFlag()}
-					>
-						<Flag />
-					</button>
-					<button class="btn btn-secondary" onclick={qm.toggleShuffle}
-						><Shuffle size="18" /> {qm.isShuffled ? 'Unshuffle' : 'Shuffle'}
-					</button>
-
-					<button
-						class="btn btn-outline"
-						onclick={qm.goToPreviousQuestion}
-						disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
-					>
-						<ArrowLeft />
-					</button>
-
-					<button
-						class="btn btn-outline"
-						onclick={qm.goToNextQuestion}
-						disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
-					>
-						<ArrowRight />
-					</button>
-				</div>
-
-				{#if qm.checkResult !== null}
-					<div
-						class="my-4 text-center font-bold {qm.checkResult === 'Correct!'
-							? 'text-green-500'
-							: 'text-red-500'}"
-					>
-						{qm.checkResult}
-					</div>
-				{/if}
-			</div>
-		{/if}
-	</div>
-
-	<!--Mobile Menu-->
-	<div
-		class="fixed bottom-0 left-0 w-full bg-base-100 shadow-lg border-t border-base-300 z-50 flex gap-2 items-center px-4 py-4 lg:hidden flex-wrap justify-center"
-	>
-		<button class="btn btn-outline btn-sm" onclick={qm.clearSelectedAnswers}>Clear</button>
-		<button class="btn btn-outline btn-success btn-sm" onclick={qm.checkAnswers}>Check</button>
-		<button
-			class="btn btn-warning btn-outline btn-sm"
-			aria-label="flag question {qm.questionIds.indexOf(qm.currentlySelectedId)}"
-			onclick={() => qm.toggleFlag()}
-		>
-			<Flag />
-		</button>
-		<div class="flex flex-row gap-2">
-			<button
-				class="btn btn-outline btn-sm"
-				onclick={qm.goToPreviousQuestion}
-				disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
-			>
-				<ArrowLeft />
-			</button>
-			<button
-				class="btn btn-outline btn-sm"
-				onclick={qm.goToNextQuestion}
-				disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
-			>
-				<ArrowRight />
-			</button>
-		</div>
-		<button class="btn modal-button lg:hidden btn-sm" onclick={() => (qm.isModalOpen = true)}
-			><Eye /></button
-		>
-
-		<div class="dropdown dropdown-top dropdown-center">
-			<div tabindex="0" role="button" class="btn btn-sm btn-soft btn-accent m-1">
-				Options <ChevronUp />
-			</div>
-			<ul
-				tabindex="-1"
-				class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
-			>
-				<li>
-					<button class="text-error" onclick={() => (qm.isResetModalOpen = true)}
-						><ListRestart size="18" />Reset</button
-					>
-				</li>
-				<li>
-					<button class="" onclick={qm.toggleShuffle}
-						><Shuffle size="18" /> {qm.isShuffled ? 'Unshuffle' : 'Shuffle'}
-					</button>
-				</li>
-				<li>
-					<button onclick={qm.toggleSortByFlagged}
-						><Flag size="16" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
-					>
-				</li>
-				<li>
-					<button onclick={qm.toggleShowIncomplete}>
-						<BookmarkCheck size="16" />
-						{qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
-					</button>
-				</li>
-			</ul>
-		</div>
-
-		<dialog class="modal max-w-full p-4" class:modal-open={qm.isModalOpen}>
-			<div class="modal-box">
-				<form method="dialog">
-					<button
-						class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
-						onclick={() => (qm.isModalOpen = false)}>✕</button
-					>
-				</form>
-				<h3 class="text-lg font-bold">Solution</h3>
-				<p class="py-4">{qm.questionSolution}</p>
-			</div>
-		</dialog>
+		<QuizContent {qm} />
 	</div>
+	<MobileMenu {qm} />
 </div>
 
 <style>
@@ -526,7 +148,4 @@
 			transform: translateY(-10px);
 		}
 	}
-	.animate-fade {
-		animation: fadeInOut 0.5s forwards;
-	}
 </style>
diff --git a/src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte b/src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte
new file mode 100644
index 0000000..cacd5f1
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte
@@ -0,0 +1,22 @@
+<script>
+	let { qm } = $props();
+	import { ArrowLeft } from 'lucide-svelte';
+</script>
+
+<div
+	class="lg:hidden flex flex-row mt-2 items-center w-full justify-between sticky top-0 bg-base-100 z-40 py-2"
+>
+	<a class="btn btn-ghost flex-shrink-0 flex items-center ms-3" href="/dashboard">
+		<ArrowLeft />
+	</a>
+
+	<div class="flex flex-row gap-2 justify-center grow">
+		<p class="font-bold tracking-wide text-secondary hidden md:block">
+			CHAPTER {qm.chapterData.chapter}
+		</p>
+		<p class="hidden md:block">·</p>
+		<h1 class="font-bold">{qm.chapterData.name}</h1>
+	</div>
+
+	<div class="flex-shrink-0 w-24"></div>
+</div>
diff --git a/src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte b/src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte
new file mode 100644
index 0000000..a2a0241
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte
@@ -0,0 +1,88 @@
+<script>
+	let { qm } = $props();
+	import {
+		Eye,
+		ArrowLeft,
+		ArrowRight,
+		ListRestart,
+		ChevronUp,
+		Flag,
+		BookmarkCheck,
+		Shuffle
+	} from 'lucide-svelte';
+</script>
+
+<div
+	class="fixed bottom-0 left-0 w-full bg-base-100 shadow-lg border-t border-base-300 z-50 flex gap-2 items-center px-4 py-4 lg:hidden flex-wrap justify-center"
+>
+	<button class="btn btn-outline btn-sm" onclick={qm.clearSelectedAnswers}>Clear</button>
+	<button class="btn btn-outline btn-success btn-sm" onclick={qm.checkAnswers}>Check</button>
+	<button
+		class="btn btn-warning btn-outline btn-sm"
+		aria-label="flag question {qm.questionIds.indexOf(qm.currentlySelectedId)}"
+		onclick={() => qm.toggleFlag()}
+	>
+		<Flag />
+	</button>
+	<div class="flex flex-row gap-2">
+		<button
+			class="btn btn-outline btn-sm"
+			onclick={qm.goToPreviousQuestion}
+			disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
+		>
+			<ArrowLeft />
+		</button>
+		<button
+			class="btn btn-outline btn-sm"
+			onclick={qm.goToNextQuestion}
+			disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
+		>
+			<ArrowRight />
+		</button>
+	</div>
+	<button class="btn modal-button lg:hidden btn-sm" onclick={() => (qm.isModalOpen = true)}
+		><Eye /></button
+	>
+
+	<div class="dropdown dropdown-top dropdown-center">
+		<div tabindex="0" role="button" class="btn btn-sm btn-soft btn-accent m-1">
+			Options <ChevronUp />
+		</div>
+		<ul tabindex="-1" class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
+			<li>
+				<button class="text-error" onclick={() => (qm.isResetModalOpen = true)}
+					><ListRestart size="18" />Reset</button
+				>
+			</li>
+			<li>
+				<button class="" onclick={qm.toggleShuffle}
+					><Shuffle size="18" /> {qm.isShuffled ? 'Unshuffle' : 'Shuffle'}
+				</button>
+			</li>
+			<li>
+				<button onclick={qm.toggleSortByFlagged}
+					><Flag size="16" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
+				>
+			</li>
+			<li>
+				<button onclick={qm.toggleShowIncomplete}>
+					<BookmarkCheck size="16" />
+					{qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
+				</button>
+			</li>
+		</ul>
+	</div>
+
+	<dialog class="modal max-w-full p-4" class:modal-open={qm.isModalOpen}>
+		<div class="modal-box">
+			<form method="dialog">
+				<button
+					class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
+					onclick={() => (qm.isModalOpen = false)}>✕</button
+				>
+			</form>
+			<h3 class="text-lg font-bold">Solution</h3>
+			<p class="py-4">{qm.questionSolution}</p>
+		</div>
+	</dialog>
+</div>
diff --git a/src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte b/src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte
new file mode 100644
index 0000000..fa8834e
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte
@@ -0,0 +1,45 @@
+<script>
+	let { qm } = $props();
+	import { Flag, Shuffle, ArrowRight, ArrowLeft } from 'lucide-svelte';
+</script>
+
+<div class=" flex-row justify-center mt-8 gap-4 hidden lg:flex">
+	<button class="btn btn-outline" onclick={qm.clearSelectedAnswers}>Clear</button>
+	<button class="btn btn-soft btn-success" onclick={qm.checkAnswers}>Check</button>
+	<button
+		class="btn btn-warning btn-soft"
+		aria-label="flag question {qm.questionIds.indexOf(qm.currentlySelectedId)}"
+		onclick={() => qm.toggleFlag()}
+	>
+		<Flag />
+	</button>
+	<button class="btn btn-secondary" onclick={qm.toggleShuffle}
+		><Shuffle size="18" /> {qm.isShuffled ? 'Unshuffle' : 'Shuffle'}
+	</button>
+
+	<button
+		class="btn btn-outline"
+		onclick={qm.goToPreviousQuestion}
+		disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
+	>
+		<ArrowLeft />
+	</button>
+
+	<button
+		class="btn btn-outline"
+		onclick={qm.goToNextQuestion}
+		disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
+	>
+		<ArrowRight />
+	</button>
+</div>
+
+{#if qm.checkResult !== null}
+	<div
+		class="my-4 text-center font-bold {qm.checkResult === 'Correct!'
+			? 'text-green-500'
+			: 'text-red-500'}"
+	>
+		{qm.checkResult}
+	</div>
+{/if}
diff --git a/src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte b/src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte
new file mode 100644
index 0000000..11f8369
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte
@@ -0,0 +1,35 @@
+<script>
+	import { Eye } from 'lucide-svelte';
+
+	let { qm, option, index } = $props();
+</script>
+
+<label
+	class="label cursor-pointer rounded-full flex items-center border-2 border-base-300 bg-base-200 transition-colors
+{qm.showSolution ? (option.isCorrect ? 'border-success' : 'border-error') : ''}"
+>
+	<input
+		type="checkbox"
+		class="checkbox checkbox-primary checkbox-sm ms-6"
+		checked={option.isSelected ? true : false}
+		onclick={() => qm.toggleOption(index)}
+		disabled={option.isEliminated || qm.showSolution}
+	/>
+	<span
+		class="flex-grow text-wrap break-words ml-8 my-4 {option.isEliminated
+			? 'line-through opacity-50'
+			: ''}"
+	>
+		{option.text}
+	</span>
+	<div class="flex items-center justify-center w-16 mr-4">
+		<button
+			class="btn btn-ghost btn-circle"
+			onclick={() => qm.toggleElimination(index)}
+			disabled={qm.showSolution}
+			aria-label="eliminate option {option.letter}"
+		>
+			<Eye />
+		</button>
+	</div>
+</label>
diff --git a/src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte b/src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte
new file mode 100644
index 0000000..8003a8d
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte
@@ -0,0 +1,38 @@
+<script>
+	let { qm } = $props();
+</script>
+
+{#if qm.fm.noFlags}
+	<!-- Alert Box (Responsive & Centered) -->
+	<div
+		role="alert"
+		class="alert alert-warning fixed top-4 sm:top-6 md:top-8 left-1/2 transform -translate-x-1/2 z-50 w-11/12 sm:w-max max-w-xs sm:max-w-sm md:max-w-md p-4 text-center animate-fade"
+	>
+		<svg
+			xmlns="http://www.w3.org/2000/svg"
+			class="h-6 w-6 shrink-0 stroke-current inline-block align-middle mr-2"
+			fill="none"
+			viewBox="0 0 24 24"
+		>
+			<path
+				stroke-linecap="round"
+				stroke-linejoin="round"
+				stroke-width="2"
+				d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
+			/>
+		</svg>
+		<span class="align-middle">No Questions Flagged.</span>
+		<button
+			class="btn btn-sm btn-ghost btn-warning"
+			onclick={() => {
+				qm.fm.noFlags = false;
+			}}>X</button
+		>
+	</div>
+{/if}
+
+<style>
+	.animate-fade {
+		animation: fadeInOut 0.5s forwards;
+	}
+</style>
diff --git a/src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte b/src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte
new file mode 100644
index 0000000..3739cd2
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte
@@ -0,0 +1,28 @@
+<script>
+	let { qm } = $props();
+</script>
+
+<!-- Question Selection Menu -->
+<div class="flex flex-row w-full mb-4 overflow-x-auto lg:mb-0 lg:mt-6 space-x-2 relative">
+	{#key qm.fm.flagCount}
+		{#each qm.getCurrentQuestionIds() as id, index}
+			<div class="indicator">
+				{#if qm.fm.flags.has(id)}
+					<span
+						class="indicator-item indicator-start badge badge-warning badge-xs !right-10 translate-x-1/4 translate-y-1/4"
+					></span>
+				{/if}
+				<button
+					class="btn btn-circle btn-soft mx-2 {qm.currentlySelectedId === id
+						? 'btn-primary'
+						: 'btn-outline'} {qm.selectedAnswers[id]?.selected?.size > 0 ? 'btn-accent' : ''}"
+					aria-label="question {index + 1}"
+					onclick={() => qm.changeSelected(id)}
+					bind:this={qm.questionButtons[index]}
+				>
+					{index + 1}
+				</button>
+			</div>
+		{/each}
+	{/key}
+</div>
diff --git a/src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte b/src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte
new file mode 100644
index 0000000..ee58578
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte
@@ -0,0 +1,52 @@
+<script>
+	let { qm } = $props();
+	import { ArrowDownNarrowWide, Flag, BookmarkCheck } from 'lucide-svelte';
+	import AnswerOptions from './AnswerOptions.svelte';
+	import ActionButtons from './ActionButtons.svelte';
+</script>
+
+{#if qm.questionMap[qm.currentlySelectedId]}
+	<div class="w-full mb-8 mt-2 overflow-y-auto max-h-[70vh] pb-16 sm:pb-0">
+		<div class="mx-4 sm:mx-6">
+			<div class="flex flex-row justify-between">
+				<div class="font-bold text-lg sm:text-xl mb-4 self-center">
+					{qm.questionMap[qm.currentlySelectedId].question_data.question}
+					<span class="text-base-content/50 font-medium text-sm ms-2"
+						>Pick {qm.correctAnswersCount}.</span
+					>
+				</div>
+
+				<div class="dropdown dropdown-end lg:block hidden">
+					<div tabindex="0" role="button" class="btn btn-soft btn-accent m-1 btn-circle">
+						<ArrowDownNarrowWide />
+					</div>
+					<ul
+						tabindex="-1"
+						class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
+					>
+						<li>
+							<button onclick={qm.toggleSortByFlagged}
+								><Flag size="16" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
+							>
+						</li>
+						<li>
+							<button onclick={qm.toggleShowIncomplete}>
+								<BookmarkCheck size="16" />
+								{qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
+							</button>
+						</li>
+					</ul>
+				</div>
+			</div>
+
+			{#key qm.refreshKey}
+				<div class="flex flex-col justify-start mt-4 space-y-4">
+					{#each qm.questionAnswerStates as option, index}
+						<AnswerOptions {qm} {option} {index} />
+					{/each}
+				</div>
+			{/key}
+		</div>
+		<ActionButtons {qm} />
+	</div>
+{/if}
diff --git a/src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte b/src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte
new file mode 100644
index 0000000..3f748bc
--- /dev/null
+++ b/src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte
@@ -0,0 +1,110 @@
+<script>
+	let { qm, admin } = $props();
+	import { Eye, Lightbulb, ArrowLeft } from 'lucide-svelte';
+</script>
+
+<div class="hidden lg:block w-1/4 lg:border-r border-base-300 overflow-y-auto">
+	<div class="flex flex-row justify-between">
+		<a class="btn btn-ghost mt-4 ms-2" href="/dashboard"> <ArrowLeft />Back</a>
+	</div>
+
+	<div class="mx-8 mt-4">
+		<p class="font-bold text-sm tracking-wide text-secondary mb-2">
+			CHAPTER {qm.chapterData.chapter}
+		</p>
+		<h1 class="text-3xl font-bold">{qm.chapterData.name}</h1>
+		<p class="text-base-content mt-2">{qm.chapterData.desc}</p>
+
+		<p class="text-base-content/60 mt-4">{qm.progress}% done.</p>
+		<p>
+			<progress
+				class="progress progress-success w-full transition-colors"
+				value={qm.progress}
+				max="100"
+			></progress>
+		</p>
+
+		<div class="flex flex-col justify-center">
+			<div class="card bg-base-100 shadow-xl mt-12">
+				<div class="card-body">
+					<div class="flex flex-row flex-wrap justify-between border-b pb-2">
+						<h2 class="card-title">Solution</h2>
+						<div class="flex flex-row">
+							<kbd class="kbd kbd-sm hidden xl:block self-center me-1">tab</kbd>
+
+							<button class="btn btn-ghost" onclick={() => qm.handleSolution()}><Eye /></button>
+						</div>
+					</div>
+
+					<p
+						class={`mt-2 transition-all duration-300 ${qm.showSolution ? 'blur-none' : 'blur-sm'}`}
+					>
+						{qm.questionSolution}
+					</p>
+				</div>
+			</div>
+
+			<div class="flex flex-row mt-12 justify-center space-x-2">
+				<button class="btn btn-error btn-soft" onclick={() => (qm.isResetModalOpen = true)}
+					>Reset</button
+				>
+
+				<div class="tooltip" data-tip="Shortcuts">
+					<div class="tooltip-content text-black font-mono flex flex-col gap-1 p-2">
+						<div class="flex items-center gap-8 justify-between">
+							<kbd class="kbd">shift + s</kbd>
+							<span class="text-white">Shuffle</span>
+						</div>
+						<div class="flex items-center gap-8 justify-between">
+							<kbd class="kbd">f</kbd>
+							<span class="text-white">Flag</span>
+						</div>
+						<div class="flex items-center gap-8 justify-between">
+							<kbd class="kbd">enter</kbd>
+							<span class="text-white">Check</span>
+						</div>
+						<div class="flex flex-col gap-1">
+							<div class="flex items-center gap-8 justify-between">
+								<kbd class="kbd">arrows</kbd>
+								<span class="text-white">Navigation</span>
+							</div>
+						</div>
+						<div class="flex items-center gap-8 justify-between">
+							<kbd class="kbd">esc</kbd>
+							<span class="text-white">Clear</span>
+						</div>
+					</div>
+					<button class="btn btn-ghost">
+						<Lightbulb />
+					</button>
+				</div>
+				{#if admin}
+					<a class="btn btn-info btn-soft" href="/admin" target="_blank">Admin</a>
+				{/if}
+			</div>
+		</div>
+	</div>
+</div>
+
+<div>
+	<dialog class="modal max-w-full p-4" class:modal-open={qm.isResetModalOpen}>
+		<div class="modal-box">
+			<form method="dialog">
+				<button
+					class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
+					onclick={() => (qm.isResetModalOpen = false)}>✕</button
+				>
+			</form>
+			<h3 class="text-lg font-bold">Reset Progress</h3>
+			<p class="py-4">
+				Do you want to start over? All current progress for this chapter will be lost.
+			</p>
+
+			<div class="flex justify-end space-x-2">
+				<button class="btn btn-outline" onclick={() => (qm.isResetModalOpen = false)}>Cancel</button
+				>
+				<button class="btn btn-error" onclick={() => qm.reset()}>Reset</button>
+			</div>
+		</div>
+	</dialog>
+</div>


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🧹 Nitpick comments (22)</summary><blockquote>

<details>
<summary>src/routes/admin/challenge/components/AddNewQuestionModal.svelte (2)</summary><blockquote>

`71-91`: **Fix the ESLint warning about unused variable.**

The static analysis tool flagged the use of `_` as an unused variable. Since you're only interested in the index, consider using Svelte's index-only syntax for the #each loop.


```diff
-{#each newQuestion.question_data.options as _, i}
+{#each newQuestion.question_data.options as option, i}
```

Or alternatively, use the index-only syntax:

```diff
-{#each newQuestion.question_data.options as _, i}
+{#each newQuestion.question_data.options.map((_, i) => i) as i}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 71-71: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

---

`102-129`: **Fix the ESLint warning about unused variable.**

Similar to the previous comment, the static analysis tool flagged the unused `_` variable. Consider using Svelte's index-only syntax here as well.


```diff
-{#each newQuestion.question_data.correct_answers as _, i}
+{#each newQuestion.question_data.correct_answers as answer, i}
```

Or alternatively:

```diff
-{#each newQuestion.question_data.correct_answers as _, i}
+{#each newQuestion.question_data.correct_answers.map((_, i) => i) as i}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 102-102: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

</blockquote></details>
<details>
<summary>src/routes/admin/challenge/components/Search.svelte (1)</summary><blockquote>

`44-54`: **Consider improving the accessibility of the chapter select.**

While the label is properly associated with the `select` element, it's placed after the control in the DOM. For optimal accessibility, consider moving the label before the select element.


```diff
<div>
+   <label for="chapter-select" class="sr-only">Select Chapter</label>
    <select id="chapter-select" class="select" bind:value={selectedChapter}>
        <option value="">All Chapters</option>
        {#each Array.from(new Set(questions.map((q) => q.chapter))).sort() as chapter}
            {#if chapter}
                <option value={chapter}>{chapter}</option>
            {/if}
        {/each}
    </select>
-   <label for="chapter-select" class="sr-only">Select Chapter</label>
</div>
```

</blockquote></details>
<details>
<summary>src/routes/admin/challenge/components/PaginationControls.svelte (1)</summary><blockquote>

`41-53`: **Consider extracting pagination logic to a separate function.**

The pagination logic in the #each block is complex and might be easier to understand if extracted into a computed property or function. This would improve readability and make the template cleaner.


```diff
<script lang="ts">
    import { ChevronLeft, ChevronRight } from 'lucide-svelte';

    let { currentPage, totalPages, questionsPerPage, totalEntries, goToPage } = $props();
+   
+   function getPageNumbers() {
+       return Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
+           let pageNum;
+           if (totalPages <= 5) {
+               pageNum = i + 1;
+           } else if (currentPage <= 3) {
+               pageNum = i + 1;
+           } else if (currentPage >= totalPages - 2) {
+               pageNum = totalPages - 4 + i;
+           } else {
+               pageNum = currentPage - 2 + i;
+           }
+           return pageNum;
+       });
+   }
</script>

<!-- Then in the template -->
{#each getPageNumbers() as pageNum}
    <button
        class="join-item btn btn-sm"
        class:btn-active={pageNum === currentPage}
        onclick={() => goToPage(pageNum)}
    >
        {pageNum}
    </button>
{/each}
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte (3)</summary><blockquote>

`11-23`: **Improve SVG accessibility**

The SVG icon should include accessibility attributes to ensure proper screen reader support.


```diff
<svg
  xmlns="http://www.w3.org/2000/svg"
  class="h-6 w-6 shrink-0 stroke-current inline-block align-middle mr-2"
  fill="none"
  viewBox="0 0 24 24"
+ aria-hidden="true"
>
```

---

`25-30`: **Add descriptive aria-label to close button**

The close button should have an aria-label for better accessibility.


```diff
<button
  class="btn btn-sm btn-ghost btn-warning"
+ aria-label="Dismiss no flags notification"
  onclick={() => {
    qm.fm.noFlags = false;
  }}>X</button
>
```

---

`5-5`: **Consider clearer naming for alert visibility flag**

The name `noFlags` is potentially confusing as it's a negative condition. Consider a clearer name like `showNoFlagsAlert` for better readability and maintainability.

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte (2)</summary><blockquote>

`9-11`: **Improve accessibility for back button**

The back button needs an accessible label to clearly communicate its purpose to screen reader users.


```diff
<a 
  class="btn btn-ghost flex-shrink-0 flex items-center ms-3" 
  href="/dashboard"
+ aria-label="Return to dashboard"
>
  <ArrowLeft aria-hidden="true" />
</a>
```

---

`21-21`: **Replace fixed-width spacer div with flex utility**

The fixed-width spacer div might not adapt well to different screen sizes. Consider using flex utilities for a more responsive layout.


```diff
- <div class="flex-shrink-0 w-24"></div>
+ <div class="flex-shrink-0 basis-[24px] md:basis-[96px]"></div>
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte (3)</summary><blockquote>

`10-14`: **Improve flag indicator accessibility**

The warning indicator badge doesn't have any text content, which might affect screen readers. Consider adding an aria-label to convey its meaning.


```diff
{#if qm.fm.flags.has(id)}
  <span
    class="indicator-item indicator-start badge badge-warning badge-xs !right-10 translate-x-1/4 translate-y-1/4"
+   aria-label="Question flagged"
  ></span>
{/if}
```

---

`15-24`: **Add aria-current for currently selected question**

Use the aria-current attribute to indicate which question is currently selected, improving accessibility.


```diff
<button
  class="btn btn-circle btn-soft mx-2 {qm.currentlySelectedId === id
    ? 'btn-primary'
    : 'btn-outline'} {qm.selectedAnswers[id]?.selected?.size > 0 ? 'btn-accent' : ''}"
  aria-label="question {index + 1}"
+ aria-current={qm.currentlySelectedId === id ? 'page' : undefined}
  onclick={() => qm.changeSelected(id)}
  bind:this={qm.questionButtons[index]}
>
```

---

`12-12`: **Use caution with the `!` CSS override**

The `!right-10` class uses the important (`!`) override which may lead to unexpected style conflicts and make future styling changes difficult.


Consider refactoring to use more specific selectors or modifying your Tailwind configuration to avoid `!` overrides where possible.

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte (2)</summary><blockquote>

`19-39`: **Improve dropdown menu accessibility and usability**

The dropdown menu lacks accessibility features and clear labeling:


```diff
<div class="dropdown dropdown-end lg:block hidden">
  <div 
    tabindex="0" 
    role="button" 
    class="btn btn-soft btn-accent m-1 btn-circle"
+   aria-label="Sort and filter options"
+   aria-haspopup="true"
  >
    <ArrowDownNarrowWide aria-hidden="true" />
+   <span class="sr-only">Sort and filter</span>
  </div>
  <ul
    tabindex="-1"
    class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
+   aria-label="Sorting and filtering options"
+   role="menu"
  >
    <li role="none">
      <button 
        onclick={qm.toggleSortByFlagged}
+       role="menuitem"
      ><Flag size="16" aria-hidden="true" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
      >
    </li>
    <li role="none">
      <button 
        onclick={qm.toggleShowIncomplete}
+       role="menuitem"
      >
        <BookmarkCheck size="16" aria-hidden="true" />
        {qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
      </button>
    </li>
  </ul>
</div>
```

---

`8-52`: **Add loading and error states**

The component doesn't include loading or error states. Consider adding appropriate feedback for users when data is loading or when errors occur.


```diff
+{#if qm.isLoading}
+  <div class="w-full flex justify-center items-center py-8">
+    <div class="loading loading-spinner loading-lg"></div>
+  </div>
+{:else if qm.hasError}
+  <div class="alert alert-error m-4">
+    <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
+    <span>Error loading question data. Please try again.</span>
+  </div>
{#if qm.questionMap[qm.currentlySelectedId]}
  <!-- existing content -->
+{:else if !qm.isLoading && !qm.hasError}
+  <div class="alert alert-info m-4">
+    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
+    <span>No question selected. Please select a question to continue.</span>
+  </div>
{/if}
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte (2)</summary><blockquote>

`11-17`: **Consider enhancing checkbox accessibility**

The checkbox input element is part of a label, but it lacks an explicit text-based association. While the label element wraps the input, adding an `aria-label` to the checkbox would further enhance accessibility.



```diff
<input
  type="checkbox"
  class="checkbox checkbox-primary checkbox-sm ms-6"
  checked={option.isSelected ? true : false}
  onclick={() => qm.toggleOption(index)}
  disabled={option.isEliminated || qm.showSolution}
+ aria-label="Select option {option.letter}"
/>
```

---

`14-14`: **Simplify boolean expression**

The ternary operation for the `checked` attribute can be simplified since it's already returning a boolean value.



```diff
- checked={option.isSelected ? true : false}
+ checked={option.isSelected}
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte (1)</summary><blockquote>

`6-35`: **Navigation button position could be improved**

The navigation buttons are currently mixed with other action buttons, which might be confusing for users. Consider grouping the navigation buttons together at one end (either left or right) to create a clearer visual hierarchy.


Consider restructuring the button layout:

```diff
<div class="flex-row justify-center mt-8 gap-4 hidden lg:flex">
+  <button
+    class="btn btn-outline"
+    onclick={qm.goToPreviousQuestion}
+    disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
+  >
+    <ArrowLeft />
+  </button>
  <button class="btn btn-outline" onclick={qm.clearSelectedAnswers}>Clear</button>
  <button class="btn btn-soft btn-success" onclick={qm.checkAnswers}>Check</button>
  <button
    class="btn btn-warning btn-soft"
    aria-label="flag question {qm.questionIds.indexOf(qm.currentlySelectedId)}"
    onclick={() => qm.toggleFlag()}
  >
    <Flag />
  </button>
  <button class="btn btn-secondary" onclick={qm.toggleShuffle}
    ><Shuffle size="18" /> {qm.isShuffled ? 'Unshuffle' : 'Shuffle'}
  </button>
- <button
-   class="btn btn-outline"
-   onclick={qm.goToPreviousQuestion}
-   disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === 0}
- >
-   <ArrowLeft />
- </button>
- <button
-   class="btn btn-outline"
-   onclick={qm.goToNextQuestion}
-   disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
- >
-   <ArrowRight />
- </button>
+  <button
+    class="btn btn-outline"
+    onclick={qm.goToNextQuestion}
+    disabled={qm.questionIds.indexOf(qm.currentlySelectedId) === qm.questions.length - 1}
+  >
+    <ArrowRight />
+  </button>
</div>
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte (3)</summary><blockquote>

`6-6`: **Consider using more specific width class**

Using a fractional width (`w-1/4`) might not be optimal for all screen sizes within the lg breakpoint. Consider using a more specific fixed width or a min/max width constraint.



```diff
-<div class="hidden lg:block w-1/4 lg:border-r border-base-300 overflow-y-auto">
+<div class="hidden lg:block w-80 lg:border-r border-base-300 overflow-y-auto">
```

Or with min/max constraints:

```diff
-<div class="hidden lg:block w-1/4 lg:border-r border-base-300 overflow-y-auto">
+<div class="hidden lg:block w-1/4 min-w-[320px] max-w-[400px] lg:border-r border-base-300 overflow-y-auto">
```

---

`33-36`: **Consider adding keyboard shortcut functionality**

The kbd element for the "tab" key is displayed, but there's no indication that it actually triggers the solution visibility toggle. Consider adding keyboard event handling to make this functional.



```diff
<script>
  let { qm, admin } = $props();
  import { Eye, Lightbulb, ArrowLeft } from 'lucide-svelte';
+ import { onMount } from 'svelte';
+
+ onMount(() => {
+   const handleKeydown = (e) => {
+     if (e.key === 'Tab') {
+       e.preventDefault();
+       qm.handleSolution();
+     }
+   };
+   window.addEventListener('keydown', handleKeydown);
+   return () => {
+     window.removeEventListener('keydown', handleKeydown);
+   };
+ });
</script>
```

---

`89-110`: **Modal implementation should use the backdrop for dismissal**

The modal implementation is good, but it doesn't allow dismissal by clicking outside the modal (on the backdrop). This is a common pattern that users expect.



```diff
<dialog class="modal max-w-full p-4" class:modal-open={qm.isResetModalOpen}>
+  <div class="modal-backdrop" onclick={() => (qm.isResetModalOpen = false)}></div>
  <div class="modal-box">
    <form method="dialog">
      <button
        class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
        onclick={() => (qm.isResetModalOpen = false)}>✕</button
      >
    </form>
    <h3 class="text-lg font-bold">Reset Progress</h3>
    <p class="py-4">
      Do you want to start over? All current progress for this chapter will be lost.
    </p>

    <div class="flex justify-end space-x-2">
      <button class="btn btn-outline" onclick={() => (qm.isResetModalOpen = false)}>Cancel</button
      >
      <button class="btn btn-error" onclick={() => qm.reset()}>Reset</button>
    </div>
  </div>
</dialog>
```

</blockquote></details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte (2)</summary><blockquote>

`43-45`: **Add descriptive text for the Eye button**

The Eye button lacks descriptive text, which might not be immediately clear to users. Consider adding a tooltip or text label to indicate its purpose.



```diff
-<button class="btn modal-button lg:hidden btn-sm" onclick={() => (qm.isModalOpen = true)}
-  ><Eye /></button
->
+<button 
+  class="btn modal-button lg:hidden btn-sm tooltip tooltip-top" 
+  data-tip="View Solution"
+  onclick={() => (qm.isModalOpen = true)}
+>
+  <Eye />
+</button>
```

---

`76-88`: **The modal implementation follows the same pattern as in Sidebar**

Just like in the Sidebar component, consider enhancing the modal to dismiss when clicking outside:



```diff
<dialog class="modal max-w-full p-4" class:modal-open={qm.isModalOpen}>
+  <div class="modal-backdrop" onclick={() => (qm.isModalOpen = false)}></div>
  <div class="modal-box">
    <form method="dialog">
      <button
        class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
        onclick={() => (qm.isModalOpen = false)}>✕</button
      >
    </form>
    <h3 class="text-lg font-bold">Solution</h3>
    <p class="py-4">{qm.questionSolution}</p>
  </div>
</dialog>
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between db9245d75550fc90cb91b5fe3d19a187d7011d3c and 7bd6db78bfa7d66d262877e219a05592c9e5e728.

</details>

<details>
<summary>📒 Files selected for processing (13)</summary>

* `src/routes/admin/challenge/+page.svelte` (6 hunks)
* `src/routes/admin/challenge/components/AddNewQuestionModal.svelte` (1 hunks)
* `src/routes/admin/challenge/components/PaginationControls.svelte` (1 hunks)
* `src/routes/admin/challenge/components/Search.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/+page.svelte` (2 hunks)
* `src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte` (1 hunks)
* `src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>src/routes/admin/challenge/components/AddNewQuestionModal.svelte</summary>

[error] 71-71: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

---

[error] 102-102: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (16)</summary><blockquote>

<details>
<summary>src/routes/admin/challenge/components/AddNewQuestionModal.svelte (1)</summary>

`20-237`: **Modal implementation looks good with proper accessibility and validation.**

The modal component is well-structured with appropriate accessibility attributes, validation logic, and clear user feedback including error handling and loading states. The component properly manages its state and cleans up after itself when closing.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 71-71: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

---

[error] 102-102: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

</details>

</details>

</details>
<details>
<summary>src/routes/admin/challenge/components/Search.svelte (1)</summary>

`11-56`: **Good responsive design with appropriate component structuring.**

The search component is well-designed with thoughtful responsive behavior using flex layouts. The implementation adjusts nicely between small and larger screens, and includes clear visual cues for users.

</details>
<details>
<summary>src/routes/admin/challenge/components/PaginationControls.svelte (1)</summary>

`7-78`: **Well-implemented pagination controls with complete functionality.**

The pagination component nicely handles both the display of current entries and navigation between pages. The implementation correctly disables buttons when appropriate and provides clear visual cues for the current page.

</details>
<details>
<summary>src/routes/admin/challenge/+page.svelte (5)</summary>

`394-394`: **Critical bug fix: Corrected database table reference.**

The database table reference has been corrected from 'pharmquestions' to 'pharmchallenge'. This is an essential fix that ensures the delete operation targets the correct table, preventing potential data inconsistency issues.

---

`5-10`: **Good component organization with proper imports.**

The refactoring introduces proper component imports for the extracted UI sections, improving code organization.

---

`420-428`: **Clean implementation of the search and pagination components.**

Replacing inline code with dedicated components enhances readability and maintainability. The bindings are correctly set up to ensure proper data flow between the parent and child components.

---

`497-511`: **Well-structured modal component integration.**

The AddNewQuestionModal component is properly integrated with all necessary bindings. This refactoring significantly simplifies the main component by extracting the complex modal logic.

---

`734-809`: **Table implementation with bottom pagination controls looks good.**

The table implementation is well-structured and the bottom pagination controls are properly integrated using the new PaginationControls component.

</details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/AnswerOptions.svelte (1)</summary>

`25-34`: **Approve the elimination button implementation**

The elimination button is well implemented with appropriate accessibility attributes and visual feedback. The use of the Eye icon makes sense for the elimination functionality, and the button is properly disabled when the solution is shown.

</details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/quiz/ActionButtons.svelte (1)</summary>

`37-45`: **Approve feedback message implementation**

The implementation of the feedback message is well done. It conditionally displays the result with appropriate color coding based on correctness, which provides clear visual feedback to the user.

</details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte (1)</summary>

`52-81`: **Approve the keyboard shortcuts tooltip implementation**

The keyboard shortcuts tooltip is well implemented, providing clear information about available shortcuts. The layout with the kbd elements and explanatory text is clean and easy to understand.

</details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte (2)</summary>

`47-74`: **Approve the dropdown menu implementation**

The dropdown menu is well implemented with clear options and appropriate icons. The use of a dropdown helps keep the mobile interface clean while still providing access to additional functionality.

---

`15-17`: <details>
<summary>❓ Verification inconclusive</summary>

**Be cautious with fixed positioning and z-index**

The component uses fixed positioning with a high z-index, which could potentially create conflicts with other UI elements. Consider using a more moderate z-index value and ensuring it works well with the rest of your application's stacking context.

---

🌐 Web query:

```web
What are best practices for managing z-index values in web applications?
```

💡 Result:

Effective management of z-index values in web applications requires understanding stacking contexts, strategic planning, and consistent patterns to prevent layering conflicts. Here are key best practices:

---

### 1. **Minimize Stacking Context Creation**
- **Avoid unnecessary properties** like `opacity`, `transform`, and `position: relative` unless required, as they create new stacking contexts and complicate layering[1][2].
- **Isolate components** by giving root elements (e.g., component containers) their own stacking contexts using `position: relative` or `absolute` with a `z-index`, ensuring child elements are contained within their parent’s context[5].

---

### 2. **Use Centralized z-index Management**
- **Define z-index values in a single file** using CSS variables or JavaScript constants. For example:
  ```css
  :root {
    --z-index-header: 100;
    --z-index-modal: 200;
    --z-index-error: 300;
  }
  ```
  This ensures consistency and simplifies debugging[3][4].
- **Auto-generate values** using tools or scripts to eliminate manual increments (e.g., assign values in steps of 100)[3].

---

### 3. **Leverage Semantic HTML Order**
- **Structure HTML elements** in the order they should appear visually, letting the DOM’s natural flow reduce reliance on `z-index`[1][2].
- Place critical elements like modals near the end of the HTML to ensure they appear above other content without excessive `z-index` values[1][5].

---

### 4. **Adopt a Layered Scale**
- Use **multiples of 100** (e.g., 100, 200, 300) for primary layers, allowing intermediate values for nested elements without conflicts:
  ```css
  .dropdown {
    z-index: calc(var(--z-index-modal) + 10); /* 210 */
  }
  ```
  This provides flexibility for related elements[3][4][6].

---

### 5. **Avoid Arbitrary High Values**
- Resist using `z-index: 9999`, as it leads to unpredictable overlaps and maintenance issues. Instead, rely on a predefined scale[1][4][7].
- Frameworks like Bootstrap use small, controlled increments (e.g., 1000–1070) for common components like modals and tooltips[4][7].

---

### 6. **Combine with Accessibility Practices**
- Use **ARIA attributes** like `aria-modal="true"` for modals to ensure screen readers interpret layering correctly[1].
- Ensure keyboard navigation order aligns with visual layering by managing `tabindex` independently of `z-index`[7].

---

### Example Implementation:
```html
<!-- Component structure -->
<div class="modal" style="position: fixed; z-index: var(--z-index-modal);">
  <button class="modal-close" style="z-index: calc(var(--z-index-modal) + 1);">
</div>
```
- **Result**: The modal appears above other content, and its close button stays within the modal’s stacking context[1][3][5].

---

By following these practices, teams reduce complexity, improve maintainability, and ensure consistent layering across large applications.

Citations:

- 1: https://blog.pixelfreestudio.com/understanding-z-index-stacking-contexts-demystified/
- 2: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index
- 3: https://dev.to/mimafogeus2/a-better-way-to-manage-z-indexes-1nf
- 4: https://www.smashingmagazine.com/2021/02/css-z-index-large-projects/
- 5: https://www.smashingmagazine.com/2019/04/z-index-component-based-web-application/
- 6: https://www.youtube.com/watch?v=u10zGmgEXnY
- 7: https://dev.to/darrenvong/is-using-z-index-in-your-css-bad-practice-14le

---



</details>

**Revisit Fixed Positioning and z-index Management**

- **Location:** `src/routes/dashboard/pharm/[slug]/components/mobile/MobileMenu.svelte` (Lines 15–17)  
- The component uses fixed positioning with a Tailwind `z-50`. While this value may be standard, it’s important to ensure it fits within a centralized layering strategy.
- Best practices suggest managing stacking contexts by:
  - Defining z-index values centrally (e.g., via CSS variables or constants with a predictable scale like multiples of 100) to maintain consistency.
  - Minimizing unnecessary stacking context creation to avoid unforeseen overlap issues.
- Please verify that the current z-index does not conflict with other UI elements, and adjust the scale if needed.

</details>
<details>
<summary>src/routes/dashboard/pharm/[slug]/+page.svelte (3)</summary>

`6-10`: **Good component modularization!**

Breaking down the UI into dedicated components (Sidebar, QuestionNavigation, MobileMenu, QuizContent, MobileInfo) improves code organization and maintainability. This modular approach will make future changes and maintenance much easier.

---

`129-130`: **Clean implementation of component integration**

The replacement of inline UI elements with modular components creates a much cleaner template structure. The consistent prop passing pattern (`{qm}` and `{admin}` where needed) makes the component relationships clear.



Also applies to: 132-132, 134-134, 136-136

---

`139-151`: <details>
<summary>✅ Verification successful</summary>

**Verify if the fadeInOut animation is still being used**

The fadeInOut animation keyframes are still defined, but there's no direct usage visible in this file. If it's used in one of the newly created components, that's fine. Otherwise, consider removing unused CSS.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the fadeInOut animation is used in any of the new components
rg -i "fadeInOut" "./src/routes/dashboard/pharm/\[slug\]/components"
```

Length of output: 157

---

Below is an additional verification step to help us decide if the fadeInOut animation is used anywhere in the repository. Since the previous search targeted a specific components folder that doesn’t exist, please run the following repository‐wide search:




**Next Steps:**
- If the repository-wide search shows that fadeInOut is referenced only in the definition (e.g. in `src/routes/dashboard/pharm/[slug]/+page.svelte`), please consider removing it.
- If it turns out that the animation is used somewhere else (perhaps in a different component or layout file), ensure that usage is documented or refactored accordingly.

Please verify these results and update the codebase as needed.



---

🏁 Script executed:

```shell
#!/bin/bash
# Search the entire repository for any references to the fadeInOut animation, which could indicate its usage.
rg -i "fadeInOut" .
```

Length of output: 353

---



</details>

**FadeInOut animation usage confirmed and in active use.**  
The repository-wide search shows that the fadeInOut keyframes defined in `src/routes/dashboard/pharm/[slug]/+page.svelte` are indeed referenced in at least one new component – specifically, in `src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte` where the animation is applied with `animation: fadeInOut 0.5s forwards;`. This confirms that the CSS is still in use, so no removal is needed.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T04:16:22Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>src/routes/admin/questions/+page.svelte (1)</summary><blockquote>

`246-246`: **Heading text clarification.**

The heading says "Users" even though this route manages "Questions." Make sure this label is correct for the page’s context.

</blockquote></details>
<details>
<summary>src/routes/admin/+page.svelte (2)</summary><blockquote>

`10-10`: **Single-column layout update**  
Switching to a single-column layout simplifies the design. If you still want responsiveness on larger screens, consider adding responsive classes (e.g. `md:grid-cols-2` or `lg:grid-cols-2`).

---

`17-22`: **Emoji-based link title & revised description**  
Using an emoji (“✏️”) is a fun, clear indicator, and the updated text is concise. If this link also supports creating new questions, consider clarifying that in the description (e.g. “Create, edit & organize”).

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7bd6db78bfa7d66d262877e219a05592c9e5e728 and c8aac87286ce48b7376b2a9781481dbd23306782.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `src/routes/admin/+page.server.ts` (1 hunks)
* `src/routes/admin/+page.svelte` (1 hunks)
* `src/routes/admin/challenge/components/Search.svelte` (1 hunks)
* `src/routes/admin/progress/+page.svelte` (2 hunks)
* `src/routes/admin/questions/+page.svelte` (2 hunks)

</details>

<details>
<summary>🔇 Additional comments (9)</summary><blockquote>

<details>
<summary>src/routes/admin/challenge/components/Search.svelte (1)</summary>

`1-60`: **Looks great!** 

The component’s structure, data binding, and accessibility features appear correct.

</details>
<details>
<summary>src/routes/admin/questions/+page.svelte (2)</summary>

`5-5`: **New icon import is consistent.**

Adding ArrowLeft from lucide-svelte aligns with the existing icon usage.

---

`238-246`: **Navigation and button layout.**

The addition of the back button and the "Delete All" button in the same row is well-structured.

</details>
<details>
<summary>src/routes/admin/+page.server.ts (1)</summary>

`1-41`: **Robust admin authentication flow.**

The user role check and redirect logic are correct. Handling both known and unknown errors is good practice.

</details>
<details>
<summary>src/routes/admin/progress/+page.svelte (2)</summary>

`4-4`: **Consistent icon usage.**

Importing ArrowLeft extends a consistent UI language for navigation.

---

`165-169`: **Flexible layout for navigation.**

The flex row with a back button and heading is a clear, user-friendly pattern.

</details>
<details>
<summary>src/routes/admin/+page.svelte (3)</summary>

`13-13`: **Hover effect enhancement**  
Adding `hover:text-primary` maintains visual consistency and offers better user feedback on hover. Nicely done.

---

`24-35`: **New challenge questions link**  
Great addition for managing challenge questions. The icon (“🧠”) and description align well with quiz functionality. No concerns here.

---

`39-39`: **Track user progress link**  
Including “⏰” is a cohesive UI choice, matching the existing style. Removing `target="_blank"` ensures navigation stays within the same tab. Looks good.  



Also applies to: 43-43

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:45:34Z

Review comments:
diff_hunk: @@ -0,0 +1,38 @@
+<script>
+	let { qm } = $props();
+</script>
+
+{#if qm.fm.noFlags}
+	<!-- Alert Box (Responsive & Centered) -->
+	<div
+		role="alert"
+		class="alert alert-warning fixed top-4 sm:top-6 md:top-8 left-1/2 transform -translate-x-1/2 z-50 w-11/12 sm:w-max max-w-xs sm:max-w-sm md:max-w-md p-4 text-center animate-fade"
+	>
+		<svg
+			xmlns="http://www.w3.org/2000/svg"
+			class="h-6 w-6 shrink-0 stroke-current inline-block align-middle mr-2"
+			fill="none"
+			viewBox="0 0 24 24"
+		>
+			<path
+				stroke-linecap="round"
+				stroke-linejoin="round"
+				stroke-width="2"
+				d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
+			/>
+		</svg>
+		<span class="align-middle">No Questions Flagged.</span>
+		<button
+			class="btn btn-sm btn-ghost btn-warning"
+			onclick={() => {
+				qm.fm.noFlags = false;
+			}}>X</button
+		>
+	</div>
+{/if}
+
+<style>
+	.animate-fade {
+		animation: fadeInOut 0.5s forwards;
+	}
path: src/routes/dashboard/pharm/[slug]/components/quiz/FlagAlert.svelte
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Define keyframes for animation**

The animate-fade class references a fadeInOut animation, but the keyframes are not defined. This could cause the animation to not work as expected.


```diff
<style>
  .animate-fade {
    animation: fadeInOut 0.5s forwards;
  }
+ 
+ @keyframes fadeInOut {
+   0% { opacity: 0; }
+   10% { opacity: 1; }
+   90% { opacity: 1; }
+   100% { opacity: 0; }
+ }
</style>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
<style>
	.animate-fade {
		animation: fadeInOut 0.5s forwards;
	}

	@keyframes fadeInOut {
		0% { opacity: 0; }
		10% { opacity: 1; }
		90% { opacity: 1; }
		100% { opacity: 0; }
	}
</style>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T04:16:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,22 @@
+<script>
+	let { qm } = $props();
+	import { ArrowLeft } from 'lucide-svelte';
+</script>
+
+<div
+	class="lg:hidden flex flex-row mt-2 items-center w-full justify-between sticky top-0 bg-base-100 z-40 py-2"
+>
+	<a class="btn btn-ghost flex-shrink-0 flex items-center ms-3" href="/dashboard">
+		<ArrowLeft />
+	</a>
+
+	<div class="flex flex-row gap-2 justify-center grow">
+		<p class="font-bold tracking-wide text-secondary hidden md:block">
+			CHAPTER {qm.chapterData.chapter}
+		</p>
+		<p class="hidden md:block">·</p>
+		<h1 class="font-bold">{qm.chapterData.name}</h1>
+	</div>
path: src/routes/dashboard/pharm/[slug]/components/mobile/MobileInfo.svelte
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling for chapter data**

There's no error handling if `qm.chapterData` is undefined or if its properties are missing. Consider adding a fallback to prevent runtime errors.


```diff
<div class="flex flex-row gap-2 justify-center grow">
  <p class="font-bold tracking-wide text-secondary hidden md:block">
-   CHAPTER {qm.chapterData.chapter}
+   CHAPTER {qm.chapterData?.chapter || 'N/A'}
  </p>
  <p class="hidden md:block">·</p>
-  <h1 class="font-bold">{qm.chapterData.name}</h1>
+  <h1 class="font-bold">{qm.chapterData?.name || 'Chapter name unavailable'}</h1>
</div>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
	<div class="flex flex-row gap-2 justify-center grow">
		<p class="font-bold tracking-wide text-secondary hidden md:block">
			CHAPTER {qm.chapterData?.chapter || 'N/A'}
		</p>
		<p class="hidden md:block">·</p>
		<h1 class="font-bold">{qm.chapterData?.name || 'Chapter name unavailable'}</h1>
	</div>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T04:16:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+<script>
+	let { qm } = $props();
+</script>
+
+<!-- Question Selection Menu -->
+<div class="flex flex-row w-full mb-4 overflow-x-auto lg:mb-0 lg:mt-6 space-x-2 relative">
+	{#key qm.fm.flagCount}
+		{#each qm.getCurrentQuestionIds() as id, index}
+			<div class="indicator">
+				{#if qm.fm.flags.has(id)}
+					<span
+						class="indicator-item indicator-start badge badge-warning badge-xs !right-10 translate-x-1/4 translate-y-1/4"
+					></span>
+				{/if}
+				<button
+					class="btn btn-circle btn-soft mx-2 {qm.currentlySelectedId === id
+						? 'btn-primary'
+						: 'btn-outline'} {qm.selectedAnswers[id]?.selected?.size > 0 ? 'btn-accent' : ''}"
+					aria-label="question {index + 1}"
+					onclick={() => qm.changeSelected(id)}
+					bind:this={qm.questionButtons[index]}
+				>
+					{index + 1}
+				</button>
+			</div>
+		{/each}
+	{/key}
+</div>
path: src/routes/dashboard/pharm/[slug]/components/quiz/QuestionNavigation.svelte
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Handle empty question list scenario**

The component doesn't handle the case when there are no questions. Consider adding a fallback UI for when `qm.getCurrentQuestionIds()` returns an empty array.


```diff
<div class="flex flex-row w-full mb-4 overflow-x-auto lg:mb-0 lg:mt-6 space-x-2 relative">
  {#key qm.fm.flagCount}
+   {#if qm.getCurrentQuestionIds().length === 0}
+     <div class="w-full text-center py-2">
+       <p class="text-base-content/70">No questions available</p>
+     </div>
+   {:else}
      {#each qm.getCurrentQuestionIds() as id, index}
        <!-- Existing content -->
      {/each}
+   {/if}
  {/key}
</div>
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
<div class="flex flex-row w-full mb-4 overflow-x-auto lg:mb-0 lg:mt-6 space-x-2 relative">
	{#key qm.fm.flagCount}
		{#if qm.getCurrentQuestionIds().length === 0}
			<div class="w-full text-center py-2">
				<p class="text-base-content/70">No questions available</p>
			</div>
		{:else}
			{#each qm.getCurrentQuestionIds() as id, index}
				<div class="indicator">
					{#if qm.fm.flags.has(id)}
						<span
							class="indicator-item indicator-start badge badge-warning badge-xs !right-10 translate-x-1/4 translate-y-1/4"
						></span>
					{/if}
					<button
						class="btn btn-circle btn-soft mx-2 {qm.currentlySelectedId === id
							? 'btn-primary'
							: 'btn-outline'} {qm.selectedAnswers[id]?.selected?.size > 0 ? 'btn-accent' : ''}"
						aria-label="question {index + 1}"
						onclick={() => qm.changeSelected(id)}
						bind:this={qm.questionButtons[index]}
					>
						{index + 1}
					</button>
				</div>
			{/each}
		{/if}
	{/key}
</div>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T04:16:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,52 @@
+<script>
+	let { qm } = $props();
+	import { ArrowDownNarrowWide, Flag, BookmarkCheck } from 'lucide-svelte';
+	import AnswerOptions from './AnswerOptions.svelte';
+	import ActionButtons from './ActionButtons.svelte';
+</script>
+
+{#if qm.questionMap[qm.currentlySelectedId]}
+	<div class="w-full mb-8 mt-2 overflow-y-auto max-h-[70vh] pb-16 sm:pb-0">
+		<div class="mx-4 sm:mx-6">
+			<div class="flex flex-row justify-between">
+				<div class="font-bold text-lg sm:text-xl mb-4 self-center">
+					{qm.questionMap[qm.currentlySelectedId].question_data.question}
+					<span class="text-base-content/50 font-medium text-sm ms-2"
+						>Pick {qm.correctAnswersCount}.</span
+					>
+				</div>
+
+				<div class="dropdown dropdown-end lg:block hidden">
+					<div tabindex="0" role="button" class="btn btn-soft btn-accent m-1 btn-circle">
+						<ArrowDownNarrowWide />
+					</div>
+					<ul
+						tabindex="-1"
+						class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm"
+					>
+						<li>
+							<button onclick={qm.toggleSortByFlagged}
+								><Flag size="16" />{qm.fm.showFlagged ? 'Show All' : 'Show Flagged'}</button
+							>
+						</li>
+						<li>
+							<button onclick={qm.toggleShowIncomplete}>
+								<BookmarkCheck size="16" />
+								{qm.showIncomplete ? 'Show All' : 'Show Incomplete'}
+							</button>
+						</li>
+					</ul>
+				</div>
path: src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add mobile equivalent for filtering options**

The dropdown menu for filtering questions is hidden on mobile screens (`lg:block hidden`), but there's no equivalent functionality for mobile users. Consider adding a mobile-friendly alternative.


You could add a mobile filter component that's visible only on smaller screens, or integrate these options into a mobile menu component.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T04:16:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,52 @@
+<script>
+	let { qm } = $props();
+	import { ArrowDownNarrowWide, Flag, BookmarkCheck } from 'lucide-svelte';
+	import AnswerOptions from './AnswerOptions.svelte';
+	import ActionButtons from './ActionButtons.svelte';
+</script>
+
+{#if qm.questionMap[qm.currentlySelectedId]}
path: src/routes/dashboard/pharm/[slug]/components/quiz/QuizContent.svelte
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling for missing question data**

The component only checks if `qm.questionMap[qm.currentlySelectedId]` exists but doesn't handle cases where internal properties might be missing. Consider adding more robust error handling.


```diff
-{#if qm.questionMap[qm.currentlySelectedId]}
+{#if qm.questionMap[qm.currentlySelectedId] && qm.questionMap[qm.currentlySelectedId].question_data?.question}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
{#if qm.questionMap[qm.currentlySelectedId] && qm.questionMap[qm.currentlySelectedId].question_data?.question}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-26T04:16:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-26T04:10:32Z
body: [vc]: #Gyd+pOz+HODnfR4Xj+wjLjU2W6fA44/f8nGku4yI9D8=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJsZWFybnRlcm1zIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2p1c3Rpbi1kYW5ncy1wcm9qZWN0cy9sZWFybnRlcm1zLzIybURQdXpnRjZKZndKankzVlJ2bUpwQmFOWHYiLCJwcmV2aWV3VXJsIjoibGVhcm50ZXJtcy1naXQtc3RhZ2UtanVzdGluLWRhbmdzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoibGVhcm50ZXJtcy1naXQtc3RhZ2UtanVzdGluLWRhbmdzLXByb2plY3RzLnZlcmNlbC5hcHAifSwicm9vdERpcmVjdG9yeSI6bnVsbH1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **learnterms** | ✅ Ready ([Inspect](https://vercel.com/justin-dangs-projects/learnterms/22mDPuzgF6JfwJjy3VRvmJpBaNXv)) | [Visit Preview](https://learnterms-git-stage-justin-dangs-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/learnterms-git-stage-justin-dangs-projects.vercel.app?via=pr-comment-feedback-link) | Mar 26, 2025 6:00pm |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-03-26T04:10:39Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request refactors the admin and dashboard interfaces using Svelte. In the admin challenge section, inline elements have been replaced with modular components for search, pagination, and adding questions, alongside a correction to the database table reference. In the dashboard section, several inline UI elements have been refactored into new components that organize the sidebar, question navigation, quiz content, mobile-specific navigation, and related quiz interactions. Additionally, a new server-side page load function for authentication and user data handling has been introduced.

## Changes
| File(s) | Change Summary |
| --- | --- |
| `src/routes/admin/challenge/+page.svelte` | Refactored the main admin challenge page by removing inline icons and logic, integrating new components (`Search`, `PaginationControls`, `AddNewQuestionModal`), and correcting the database table reference. |
| `src/routes/admin/challenge/components/{AddNewQuestionModal.svelte, PaginationControls.svelte, Search.svelte}` | Added modular components: a modal for adding new questions with dynamic option handling, a pagination control with page navigation and selection logic, and a search/filter component for chapter-based searching. |
| `src/routes/dashboard/pharm/[slug]/+page.svelte` | Overhauled the dashboard page by removing multiple inline lucide-svelte icons and introducing new components (`Sidebar`, `QuestionNavigation`, `MobileMenu`, `QuizContent`, `MobileInfo`) to support a modular, responsive layout; also removed the `animate-fade` class. |
| `src/routes/dashboard/pharm/[slug]/components/mobile/{MobileInfo.svelte, MobileMenu.svelte}` | Introduced mobile-specific components: one displaying a navigation bar with chapter details and a back button, and another providing a responsive mobile menu with navigation and modal support. |
| `src/routes/dashboard/pharm/[slug]/components/quiz/{ActionButtons.svelte, AnswerOptions.svelte, FlagAlert.svelte, QuestionNavigation.svelte, QuizContent.svelte}` | Created quiz components to manage interactive buttons for answer actions, display answer options with state handling, render alerts for flagged questions, enable horizontal question navigation, and encapsulate quiz content display with sorting and filtering features. |
| `src/routes/dashboard/pharm/[slug]/components/sidebar/Sidebar.svelte` | Added a sidebar component that displays chapter details, progress indicators, and navigation elements including a reset modal and admin-specific actions. |
| `src/routes/admin/+page.server.ts` | Introduced a new server-side page load function for authentication, fetching user data, and handling errors with redirects based on user roles. |
| `src/routes/admin/+page.svelte` | Simplified admin controls layout, updated links with emojis, and removed the target attribute from certain links for improved navigation. |
| `src/routes/admin/progress/+page.svelte` | Added an `ArrowLeft` icon and restructured the header to include a back navigation option. |
| `src/routes/admin/questions/+page.svelte` | Added an `ArrowLeft` icon and restructured the button layout for improved navigation. |

## Possibly related PRs
- **jdang00/learnterms#25**: The changes in the main PR are related to the introduction of the `AddNewQuestionModal` and `PaginationControls` components, which are also present in the retrieved PR, indicating a shared focus on enhancing the UI for managing questions and pagination.
- **jdang00/learnterms#27**: The changes in the main PR are related to the introduction of the `AddNewQuestionModal` component, which is also present in the retrieved PR, indicating a direct connection in functionality related to managing questions.
- **jdang00/learnterms#9**: The changes in the main PR are related to the introduction of the `AddNewQuestionModal` and `PaginationControls` components, which are also referenced in the retrieved PR as part of the admin functionalities, indicating a shared focus on enhancing the admin interface for managing questions.

## Poem
> I'm a happy little rabbit, hopping through lines of code,  
> Modular leaps and bounds where new features are bestowed.  
> From search to modal, my steps are light and free,  
> In dashboards and challenges, I find simplicity.  
> Cheers to refactors that make our code garden glow! 🐰

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fee77531caef439a678060d8b1e0763a9b2409a8 and 115c2196166f6c346c49155c15d97dc547b5caed.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/routes/admin/questions/+page.svelte` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* src/routes/admin/questions/+page.svelte

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=jdang00/learnterms&utm_content=28):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR