Implement local browser caching for shared groups and UI improvements
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m10s

This commit is contained in:
Elijah 2026-07-10 16:01:22 -07:00
parent 42ed0d275b
commit 7af0935657
37 changed files with 6141 additions and 411 deletions

View file

@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useState, useEffect } from "react";
import { parseAndRepairJson } from "@/lib/jsonRepair";
import {
flashcardImportSchema,
@ -23,12 +23,35 @@ interface PreviewData {
data: unknown;
}
interface MaterialGroup {
id: string;
name: string;
}
export function ImportTab({ classId, importType, onImported }: ImportTabProps) {
const [jsonText, setJsonText] = useState("");
const [name, setName] = useState("");
const [preview, setPreview] = useState<PreviewData | null>(null);
const [error, setError] = useState("");
const [importing, setImporting] = useState(false);
const [materialGroups, setMaterialGroups] = useState<MaterialGroup[]>([]);
const [selectedGroupId, setSelectedGroupId] = useState<string>("");
useEffect(() => {
async function fetchGroups() {
try {
const typeParam = importType === "flashcards" ? "DECK" : "QUIZ";
const res = await fetch(`/api/material-groups?classId=${classId}&type=${typeParam}`);
if (res.ok) {
const data = await res.json();
setMaterialGroups(data);
}
} catch (err) {
console.error("Failed to fetch material groups", err);
}
}
fetchGroups();
}, [classId, importType]);
function handleJsonChange(text: string) {
setJsonText(text);
@ -101,6 +124,7 @@ export function ImportTab({ classId, importType, onImported }: ImportTabProps) {
classId,
data: preview.data,
name: name.trim(),
groupId: selectedGroupId || null,
}),
});
@ -136,6 +160,25 @@ export function ImportTab({ classId, importType, onImported }: ImportTabProps) {
/>
</div>
{/* Group field */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-1.5">
Group (Optional)
</label>
<select
value={selectedGroupId}
onChange={(e) => setSelectedGroupId(e.target.value)}
className="w-full px-3.5 py-2.5 rounded-lg border border-border bg-bg-surface-alt/50 text-text-heading focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all duration-200"
>
<option value="">Uncategorized</option>
{materialGroups.map((group) => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</select>
</div>
{/* JSON paste area */}
<div>
<label className="block text-sm font-medium text-text-secondary mb-1.5">