integratedhub/islands/AdminPage.tsx
2026-02-14 02:20:19 +00:00

247 lines
7.6 KiB
TypeScript

// islands/QuizConfigure.tsx
import { useEffect } from "preact/hooks";
import { KvObj, QuizConfig } from "../utils/types.ts";
import { Signal } from "@preact/signals";
import { AsyncYesNoPop } from "./AsyncYesNoPop.tsx";
import { User } from "../db/interfaces.ts";
export interface AdminProps {
allUsers: User[];
allQuizs: KvObj<QuizConfig>[];
}
export default function AdminPage(
{ allUsers, allQuizs }: AdminProps,
) {
// Theme classes are defined in styles.css with @media (prefers-color-scheme: dark)
const [_ask, popupElement] = AsyncYesNoPop();
const _changeMode = new Signal<{ mode: string } | null>(null);
const users = new Signal<User[]>(allUsers);
const _nameError = new Signal(false);
const _randomOrder = new Signal(true);
const _modeType = new Signal("photo-see-bisaya");
const userContext = new Signal((() => {
if (allUsers && allUsers.length) {
return allUsers[0].id;
} else {
return "";
}
})());
//const [isLoading, setIsLoading] = useState(true);
const count = new Signal(0);
// const handlePop = async () => {
// const answer = await ask("Do you want to continue?");
// if (answer) {
// alert("You chose YES!");
// // ...continue logic
// } else {
// alert("You chose NO!");
// // ...alternative logic
// }
// };
const handleClick = () => {
const newCount = count.value + 1;
count.value = newCount;
};
useEffect(() => {
//console.log("allUsers",allUsers.length);
}, []);
const uploadFile = () => {
const input = document.createElement("input");
input.type = "file";
input.accept = ".txt";
// Safari fix: input must be in DOM for change event to fire reliably
input.className = "hidden-file-input";
document.body.appendChild(input);
const cleanup = () => {
if (input.parentNode) {
document.body.removeChild(input);
}
};
input.addEventListener("change", async (e) => {
console.log("uploading file");
const file = (e.target as HTMLInputElement).files?.[0];
console.log("file", file);
if (file) {
const filePeaces = (file.name as string)?.split(".txt")[0]?.trim()
.split(" ");
const _newQuizName = filePeaces[0];
//console.log("context id", userContext.value);
const text = (await file.text())?.trim();
if (userContext.value && text && text.length > 0) {
const obj: KvObj<QuizConfig> = JSON.parse(text) as KvObj<QuizConfig>;
console.log(obj.value?.quiz_name);
obj.key[0] = "quiz_configs";
obj.key[1] = "" + userContext.value;
obj.key[2] = crypto.randomUUID();
if (obj.value) {
obj.value.owner_id = obj.key[1];
obj.value.quiz_config_id = obj.key[2];
if (obj.value.questions) {
obj.value.questions.forEach((question) => {
question.owner_id = obj.key[1];
question.quiz_id = obj.key[2];
});
}
// const textToQuiz = new TextToQuizTranslations(quizData);
// const parsedQuizData = textToQuiz.parseText(text);
try {
const response = await fetch("/api/quiz-up", {
method: "POST",
body: JSON.stringify(obj),
});
if (!response.ok) {
throw new Error("Upload failed");
}
const _data = await response.json();
} catch (err) {
console.error(err);
console.log("Failed to fetch create quiz.");
}
}
}
}
cleanup();
});
// Cleanup if user cancels file dialog (focus returns to window)
const handleFocus = () => {
setTimeout(cleanup, 300);
globalThis.removeEventListener("focus", handleFocus);
};
globalThis.addEventListener("focus", handleFocus);
input.click();
};
const downloadFile = () => {
allQuizs.forEach((quiz) => {
let quizName = `${JSON.stringify(quiz.key)}.txt`;
if (quiz.value) {
quizName = `${quiz.value.quiz_name} ${quizName}.txt`;
}
const userConfirmed = globalThis.confirm(`Download? ${quizName}`);
if (userConfirmed) {
const content = JSON.stringify(quiz);
const blob = new Blob([content], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = quizName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
});
};
return (
<>
{popupElement}
<div className="theme-container rounded-xl shadow-2xl p-8 max-w-md w-full">
<div hidden>
<button type="button" onClick={handleClick}>Increment</button>
<p>Count: {count}</p>
</div>
<h2 className="text-2xl theme-text font-bold mb-4">Configure Quiz</h2>
<div className="w-[300px] mx-0 px-0">
<div className="flex space-x-2 pb-[10px]">
<button
type="button"
onClick={uploadFile}
className="flex-1 theme-button py-2 px-2 rounded-md transition duration-300"
>
Add from file
</button>
<button
type="button"
onClick={downloadFile}
className="flex-1 theme-button py-2 px-2 rounded-md transition duration-300"
>
Out to file
</button>
</div>
<div className="mb-4">
<label htmlFor="quizMode" className="block theme-text mb-2">
Admin Page
</label>
<select
id="quizMode"
value={"" + userContext.value}
onChange={(e) => {
userContext.value = e.currentTarget.value;
//handleOwnerChange(e, selectedUser);
// if (selectedUser) {
// thisQuizConfig.owner_id = selectedUser.id;
// setQuizConfig(thisQuizConfig);
// }
// setChangeMode({ mode:"session" });
// setQuizConfigStr(JSON.stringify({ theUpdate:{ mode:"session" }, quiz: thisQuizConfig, kvObj:kvObj,quizOrder:thisQuizOder }));
}}
className="w-full px-3 py-2 theme-select rounded-md"
>
{users.value.map((user: User) => (
<option
key={user.id}
className="text-black"
value={user.id}
>
{user.name}
</option>
))}
</select>
</div>
</div>
{
/* <form
hidden
action="/dashboard"
method="POST"
>
<input
type="hidden"
name="pageConfigData"
value={postDataStr}
ref={postInputValueRef}
/>
<button hidden ref={postButtonRef} type="submit">
</button>
</form> */
}
</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>
</>
);
}