373 lines
13 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|