integratedhub/islands/ArrangeQuiz.tsx
2026-01-20 09:02:26 -06:00

373 lines
13 KiB
TypeScript

// islands/QuizConfigure.tsx
import { useEffect, useState } from "preact/hooks";
import { QuizConfig } from "../utils/types.ts";
import { AsyncYesNoPop } from "./AsyncYesNoPop.tsx";
export interface ArrangementProps {
quizConfig: QuizConfig;
// kvObj: KvObj<QuizConfig>;
quizOrder: { index: number; slots: string[] };
quizMode: string;
tabs: string[];
}
const NEW_TAB_VALUE = "__new_tab__";
export default function ArrangeQuiz(
{ quizConfig, quizOrder, quizMode, tabs }: ArrangementProps,
) {
// Theme classes are defined in styles.css with @media (prefers-color-scheme: dark)
const [ask, popupElement] = AsyncYesNoPop();
const [thisQuizConfig, setQuizConfig] = useState<QuizConfig>(quizConfig);
const [orderedQuizStr, setOrderQuizStr] = useState<string>(
JSON.stringify(thisQuizConfig),
);
const [thisQuizConfigStr, setQuizConfigStr] = useState<string>(
JSON.stringify({
theUpdate: null,
quiz: quizConfig,
//kvObj: kvObj,
quizOrder: quizOrder,
}),
);
const [selectedSlot, setSelectedSlot] = useState(0);
const [thisQuizOder, setThisQuizOrder] = useState(quizOrder);
const [randomOrder, setRandomOrder] = useState(true);
const [modeType, setModeType] = useState(quizMode);
//const [isLoading, setIsLoading] = useState(true);
// Tab selection state
const [selectedTabIndex, setSelectedTabIndex] = useState<
number | typeof NEW_TAB_VALUE
>(-1);
const [newTabName, setNewTabName] = useState("");
const isNewTab = selectedTabIndex === NEW_TAB_VALUE;
const isExistingTab = typeof selectedTabIndex === "number" &&
selectedTabIndex >= 0;
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((prev) => {
const newCount = prev + 1;
return newCount;
});
};
const handleSaveQuiz = async (event: Event) => {
//console.log("Clicked save");
//if (changeMode && changeMode.mode) {
// setQuizConfigStr(JSON.stringify({ theUpdate:changeMode.mode, quiz: thisQuizConfig, kvObj:kvObj,quizOrder:thisQuizOder }));
// handleClick();
event.preventDefault();
//value={JSON.stringify({ theUpdate:null, quiz: quiz , kvObj:quizProps[index] ? cloneObjectExcept(quizProps[index],["value"]) as KvObj<QuizConfig>: null})}
const form = event.currentTarget as HTMLFormElement;
const userConfirmed = await ask(
"Do you want to save this change?",
Date.now(),
);
if (userConfirmed) {
form.submit();
}
//const userConfirmed = window.confirm("Do you wanted to save changes?");
// if (userConfirmed) {
// (event.currentTarget as HTMLFormElement).submit();
// }
//} else {
// event.preventDefault();
//}
};
useEffect(() => {
//console.log("setting mode!!",quizConfig.quiz_mode)
if (quizConfig.quiz_mode) {
setModeType(quizConfig.quiz_mode);
}
}, []);
useEffect(() => {
// Initialize selectedTabIndex to 0 when tabs are available and no tab is selected
if (tabs.length > 0 && selectedTabIndex === -1) {
setSelectedTabIndex(0);
}
}, [tabs, selectedTabIndex]);
return (
<>
{popupElement}
<div
className={`theme-container p-2 sm:p-3 md:p-4 rounded-lg shadow-md max-w-md mx-auto w-full overflow-auto`}
>
<div hidden>
<button onClick={handleClick}>Increment</button>
<p>Count: {count}</p>
</div>
<h2 className="text-2xl font-bold mb-4 theme-text">
Arrange Quizzes
</h2>
<div className="theme-secondary-container p-2 sm:p-3 md:p-4 rounded-lg shadow-md max-w-md mx-auto w-full overflow-auto">
<h2 className="text-2xl font-bold mb-4 theme-text">
Dashboard Quiz List
</h2>
<form
action="/language/arrange"
method="POST"
onSubmit={handleSaveQuiz}
>
<input
type="hidden"
name="quizConfigData"
value={thisQuizConfigStr}
/>
<input
type="hidden"
name="quizMode"
value={quizMode}
/>
{/* Step 1: Select a quiz */}
<div className="mb-4">
<label
htmlFor="quizSelect"
className="block mb-2 theme-text"
>
Select Quiz
</label>
<select
id="quizSelect"
value={thisQuizOder.index}
onChange={(e: any) => {
const newIndex = parseInt(e.target.value, 10);
setSelectedSlot(newIndex);
thisQuizOder.index = newIndex;
setThisQuizOrder({ ...thisQuizOder });
// Reset tab selection when quiz changes
setSelectedTabIndex(-1);
setNewTabName("");
setQuizConfigStr(
JSON.stringify({
theUpdate: newIndex,
quiz: thisQuizConfig,
quizOrder: thisQuizOder,
}),
);
}}
className="w-full px-3 py-2 rounded-md theme-select"
>
<option value={-1}>Select a quiz to move</option>
{thisQuizOder.slots.map((name: string, i: number) => (
<option
key={i}
className="text-black"
value={i}
>
{`${i} - ${name}`}
</option>
))}
</select>
</div>
{/* Step 2: Select a tab (only shown after quiz is selected) */}
{thisQuizOder.index != -1 && (
<div className="mb-4">
<label
htmlFor="tabSelect"
className="block mb-2 theme-text"
>
Select Tab
</label>
<select
id="tabSelect"
value={selectedTabIndex === NEW_TAB_VALUE
? NEW_TAB_VALUE
: selectedTabIndex}
onChange={(e: any) => {
const value = e.target.value;
if (value === NEW_TAB_VALUE) {
setSelectedTabIndex(NEW_TAB_VALUE);
setNewTabName("");
} else {
setSelectedTabIndex(parseInt(value, 10));
setNewTabName("");
}
}}
className="w-full px-3 py-2 rounded-md theme-select"
>
<option value={-1}>Select a tab</option>
{tabs.map((tabName: string, i: number) => (
<option
key={i}
className="text-black"
value={i}
>
{tabName}
</option>
))}
<option
value={NEW_TAB_VALUE}
className="text-black font-semibold"
>
+ New Tab
</option>
</select>
</div>
)}
{/* Step 3a: New tab name input (only shown if "New Tab" is selected) */}
{thisQuizOder.index != -1 && isNewTab && (
<div className="mb-4">
<label
htmlFor="newTabName"
className="block mb-2 theme-text"
>
New Tab Name
</label>
<input
type="text"
id="newTabName"
value={newTabName}
onChange={(e: any) => setNewTabName(e.target.value)}
placeholder="Enter new tab name"
className="w-full px-3 py-2 rounded-md theme-input border theme-border"
/>
</div>
)}
{/* Step 3b: Position selector (only shown if existing tab is selected) */}
{thisQuizOder.index != -1 && isExistingTab && (
<div className="mb-4">
<label
htmlFor="quizOrder"
className="block mb-2 theme-text"
>
Select Position in Tab
</label>
<select
id="quizOrder"
value={selectedSlot}
onChange={(e: any) => {
setSelectedSlot(parseInt(e.target.value, 10));
setQuizConfigStr(
JSON.stringify({
theUpdate: parseInt(e.target.value, 10),
quiz: thisQuizConfig,
quizOrder: thisQuizOder,
}),
);
}}
className="w-full px-3 py-2 rounded-md theme-select"
>
{thisQuizOder.slots.map((name: string, i: number) => (
<option
key={i}
className="text-black"
value={i}
>
{`Position ${i + 1}`}
</option>
))}
</select>
</div>
)}
{/* Save button for moving to new tab */}
{thisQuizOder.index != -1 && isNewTab &&
newTabName.trim().length > 0 && (
<div className="flex space-x-2 mt-4">
<button
type="submit"
className="theme-button px-4 py-2 rounded w-full"
onClick={() => {
setQuizConfigStr(
JSON.stringify({
theUpdate: thisQuizOder.index,
quiz: thisQuizConfig,
quizOrder: thisQuizOder,
tabAction: "new",
newTabName: newTabName.trim(),
newTabIndex: tabs.length,
}),
);
}}
>
Move to New Tab
</button>
</div>
)}
{/* Save button for reordering position within tab */}
{thisQuizOder.index != -1 && isExistingTab &&
thisQuizOder.index !== selectedSlot && (
<div className="flex space-x-2 mt-4">
<button
type="submit"
className="theme-button px-4 py-2 rounded w-full"
onClick={() => {
setQuizConfigStr(
JSON.stringify({
theUpdate: selectedSlot,
quiz: thisQuizConfig,
quizOrder: thisQuizOder,
tabAction: "reorder",
targetTabIndex: selectedTabIndex,
}),
);
}}
>
Save Position Change
</button>
</div>
)}
{/* Save button for moving to different existing tab (same position) */}
{thisQuizOder.index != -1 && isExistingTab &&
thisQuizOder.index === selectedSlot && (
<div className="flex space-x-2 mt-4">
<button
type="submit"
className="theme-button px-4 py-2 rounded w-full"
onClick={() => {
setQuizConfigStr(
JSON.stringify({
theUpdate: thisQuizOder.index,
quiz: thisQuizConfig,
quizOrder: thisQuizOder,
tabAction: "move",
targetTabIndex: selectedTabIndex,
targetTabName: tabs[selectedTabIndex as number],
}),
);
}}
>
Move to Tab
</button>
</div>
)}
</form>
</div>
</div>
<div class="fixed bottom-4 inset-x-0 flex justify-center z-40 pointer-events-none">
<div class="flex gap-2 w-11/12 max-w-md pointer-events-auto">
<span class={`w-[200px] py-3 px-6`}></span>
<span class={`w-[16px]`}></span>
<button
type="button"
onClick={() => {
globalThis.location.href = "/language/dashboard";
}}
class={`w-[200px] theme-button font-semibold py-3 px-6 rounded-full shadow-lg text-center`}
>
Dashboard
</button>
</div>
</div>
</>
);
}