first commit; dx 1.0 beta

This commit is contained in:
2025-11-09 22:35:16 +03:00
commit 4d01725955
153 changed files with 6504 additions and 0 deletions
+294
View File
@@ -0,0 +1,294 @@
import { motion, AnimatePresence, LayoutGroup } from "motion/react";
import { useAtom } from "jotai";
import { useState, useEffect } from "react";
import Logo from "../assets/logo.svg";
import { AdminUserFRequest, classUserFRequest } from "../trash/req_mgr";
import {
BiCalendar,
BiCalendarEdit,
BiCheck,
BiExit,
BiPencil,
BiPlus,
BiRedo,
BiTime,
BiTrash,
BiUndo,
BiX,
} from "react-icons/bi";
import { formatDate, parseApiDate } from "../trash/dengi";
import {
DengiButton,
DengiYesOrNoDialog,
// CoolMouseBg
} from "../trash/trash-components";
import { TableEditor } from "../trash/editorTable";
import { ifstyle } from "../trash/helpers";
import { API_BASE_URL } from "../App";
export default function EditorPage(props) {
const [classId, _setUserId] = useAtom(props.classIDAtom);
const [adminId, _setAdminId] = useAtom(props.adminAtom);
const [route, setRoute] = useAtom(props.routeAtom);
const dateRaw = props.secondPathSegment(route);
useEffect(() => {
if (
dateRaw == "" ||
adminId == "" ||
!RegExp(/\d{2}-\d{2}-\d{2}$/gm).test(dateRaw)
) {
props.setsetRoute(setRoute, "/app");
}
}, [dateRaw]);
const date = parseApiDate(dateRaw);
const [userFrNames, setUserFrNames] = useState({
admin: false,
class: false,
});
useEffect(() => {
(async () => {
let cancelled = false;
const promises = [];
if (classId !== "")
promises.push(
props.dengi_cash.withCacheAsync(`userfr-${classId}`, async () =>
classUserFRequest(API_BASE_URL, classId)
)
);
else promises.push(Promise.resolve([0]));
if (adminId !== "")
promises.push(
props.dengi_cash.withCacheAsync(`adminfr-${adminId}`, async () =>
AdminUserFRequest(API_BASE_URL, adminId)
)
);
else promises.push(Promise.resolve([0]));
Promise.all(promises).then(([classRes, adminRes]) => {
if (cancelled) return;
const classF = classRes[0] === 1 ? classRes[1].userclassname : false;
const adminF = adminRes[0] === 1 ? adminRes[1].adminName : false;
setUserFrNames({ admin: adminF, class: classF });
});
return () => {
cancelled = true;
};
})();
}, [classId, adminId]);
// on rerenderr
useEffect(() => {
console.log("editor cache clean");
props.dengi_cash.delete(
"editor_schedule",
"editor_changes",
"editor_rings"
);
}, []); // clean on full rerender
// tabs
const tabs = [
[
<>
<BiCalendarEdit />
замены
</>,
<>{/* content */}</>,
],
[
<>
<BiCalendar />
расписание
</>,
<>{/* content */}</>,
],
[
<>
<BiTime />
звонки
</>,
<>{/* content */}</>,
],
];
const [selectedTab, selectTab] = useState(0);
const [changesWereMade, setChangesWereMade] = useState(false);
const [tabDiagShow, setTabDiagShow] = useState(false);
const [tabDiagRequestTab, setTabDiagRequestTab] = useState(0); // 404 for exit
return (
<>
<div className="overflow-x-hidden">
<DengiYesOrNoDialog
show={tabDiagShow}
content={{
message: "изменения будут удалены. продолжить?",
yesBtn: "да",
noBtn: "нет",
yesIcon: <BiTrash />,
noIcon: <BiX size={20} />,
bgDissmis: true,
yesBtnStyle: "bg-red-500! text-white! md:hover:bg-red-600!",
}}
onAnswer={(answ) => {
if (answ && tabDiagRequestTab == 404) location.hash = "/app";
else if (answ) selectTab(tabDiagRequestTab);
setTabDiagShow(0);
setTabDiagRequestTab(0);
}}
/>
</div>
<div className="w-full h-screen flex flex-col items-center justify-center z-5">
<motion.div
className="w-screen h-screen flex flex-col items-center sm:max-w-[500px] sm:max-h-[90vh] gap-3"
initial={{ scale: 0.99, opacity: 0, y: 5 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
>
<motion.div
className="flex items-center w-full justify-between px-5"
layout="position"
>
<motion.img
src={Logo}
alt="logo"
width={100}
draggable={false}
layout="position"
/>
<motion.div className="flex gap-5 items-center">
<motion.span
className="font-light flex gap-2 items-center"
variants={{ hidden: { opacity: 0 }, show: { opacity: 1 } }}
initial="hidden"
animate="show"
exit="hidden"
>
<span>
{userFrNames["class"] ? userFrNames["class"] : classId}
</span>
<span className="block h-[1px] w-[20px] -rotate-45 bg-zinc-600" />
<span>
{userFrNames["admin"] ? userFrNames["admin"] : adminId}
</span>
</motion.span>
<motion.span
layout="position"
className={"rounded-full w-fit h-fit"}
></motion.span>
</motion.div>
</motion.div>
<motion.div
layout="position"
className="flex w-full px-5 flex-col gap-5"
>
<div className="flex w-full justify-between">
<div
className={
"flex items-center gap-2 transition-colors " +
ifstyle(
selectedTab == 2,
"text-transparent pointer-events-none select-none"
)
}
>
<BiPencil size={25} />
{formatDate(date)}
</div>
<div>
<a
href="#/app"
className="flex items-center justify-center border p-1 gap-2 px-2 lg:hover:border-white cursor-pointer rounded-xl transition-colors border-zinc-700 text-zinc-400 lg:hover:text-white"
onClick={(e) => {
if (changesWereMade) {
e.preventDefault();
setTabDiagRequestTab(404);
setTabDiagShow(true);
}
}}
>
<BiExit /> назад
</a>
</div>
</div>
<div className="flex items-center justify-center gap-5 select-none bg-grain bg-zinc-800/35 p-2 rounded-xl">
<LayoutGroup>
{tabs.map((item, idx) => (
<div
className={
"flex items-center justify-center gap-1 p-2 rounded-2xl md:hover:bg-white/10 transition-colors relative md:hover:text-white cursor-pointer " +
(idx == selectedTab ? "text-white" : "text-zinc-500")
}
key={idx}
onClick={() => {
if (selectedTab == idx) return;
if (!changesWereMade) {
selectTab(idx);
} else {
setTabDiagRequestTab(idx);
setTabDiagShow(true);
}
}}
>
{item}
{idx == selectedTab && (
<motion.div
layoutId="editor_tabs_underline"
className="absolute -bottom-[4px] left-0 h-[3px] bg-white/70 rounded-full w-full"
/>
)}
</div>
))}
</LayoutGroup>
</div>
<motion.div className="flex flex-col gap-2 pb-10">
{
<TableEditor
{...{
selectedTab,
setChangesWereMade,
date,
adminId,
}}
dengiCash={props.dengi_cash}
/>
}
</motion.div>
</motion.div>
</motion.div>
{/* <CoolMouseBg /> */}
</div>
<DefaultEditorBG />
</>
);
}
function DefaultEditorBG() {
return (
<div
className={
"w-full h-screen bg-gradient-to-br fixed top-0 left-0 via-black via-50% gap-5 overflow-hidden from-white/2 to-blue-500/10 -z-5 bg-fixed"
}
/>
);
}
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3
// ВАНЕЧКА ПИКМИЛЁВ <3