bug fixes
This commit is contained in:
+186
-162
@@ -19,7 +19,7 @@ import {
|
||||
classUserFRequest,
|
||||
RaspGet,
|
||||
} from "../trash/req_mgr";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { AnimatePresence, LayoutGroup, motion } from "motion/react";
|
||||
|
||||
import { apiFormatDate, isTimeBeforeNow } from "../trash/dengi";
|
||||
import { useAtom } from "jotai";
|
||||
@@ -90,7 +90,6 @@ export function DX4App({
|
||||
class: false,
|
||||
});
|
||||
|
||||
const [classLinkCopied, setClassLinkCopied] = useState(false);
|
||||
const lessonsEndedTriggered = useRef(false);
|
||||
|
||||
//TODO: Auto change day v2
|
||||
@@ -151,6 +150,37 @@ export function DX4App({
|
||||
})();
|
||||
}, [classId, adminId]);
|
||||
|
||||
const handleKeyPress = (event) => {
|
||||
switch (event.key) {
|
||||
case "a":
|
||||
case "ArrowLeft":
|
||||
setSelectedDate((prevDate) => {
|
||||
const newDate = new Date(prevDate);
|
||||
newDate.setDate(newDate.getDate() - 1);
|
||||
return newDate;
|
||||
});
|
||||
break;
|
||||
case "d":
|
||||
case "ArrowRight":
|
||||
setSelectedDate((prevDate) => {
|
||||
const newDate = new Date(prevDate);
|
||||
newDate.setDate(newDate.getDate() + 1);
|
||||
return newDate;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyPress);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyPress);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (classId === "") {
|
||||
setsetRoute(setRoute, "/");
|
||||
}
|
||||
@@ -167,7 +197,6 @@ export function DX4App({
|
||||
"flex items-center w-full justify-between px-5" +
|
||||
ifstyle(openUserSettings, " pt-5")
|
||||
}
|
||||
layout="position"
|
||||
>
|
||||
<motion.img
|
||||
src={Logo}
|
||||
@@ -214,125 +243,53 @@ export function DX4App({
|
||||
/>
|
||||
</motion.span>
|
||||
<AnimatePresence mode="popLayout">
|
||||
{openUserSettings && (
|
||||
<motion.div
|
||||
className="flex flex-col bg-grain gap-2 bg-zinc-700/20 p-2 rounded-2xl"
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 10, filter: "blur(2px)" },
|
||||
show: { opacity: 1, y: 0, filter: "blur(0)" },
|
||||
}}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
exit="hidden"
|
||||
>
|
||||
{[
|
||||
// icon
|
||||
// text
|
||||
// onclick
|
||||
// condition
|
||||
[
|
||||
<BiExit />,
|
||||
"выход",
|
||||
() => {
|
||||
setUserId("");
|
||||
setsetRoute(setRoute, "/");
|
||||
},
|
||||
true,
|
||||
],
|
||||
// [
|
||||
// <BiStar />,
|
||||
// "пикми режим",
|
||||
// () => {
|
||||
// setPickmeMode(!pickmeMode);
|
||||
// },
|
||||
// true,
|
||||
// ],
|
||||
[
|
||||
<BiSolidUser />,
|
||||
`админ ${
|
||||
userFrNames["admin"] ? userFrNames["admin"] : adminId
|
||||
}`,
|
||||
() => {},
|
||||
adminId != "",
|
||||
],
|
||||
[
|
||||
<BiLink />,
|
||||
classLinkCopied ? "скопированно" : "коп. ссылку",
|
||||
() => {
|
||||
copyToClipboard(
|
||||
location.href.substring(
|
||||
0,
|
||||
location.href.search("#")
|
||||
) +
|
||||
"#//" +
|
||||
classId
|
||||
).then((a) => {
|
||||
if (a) {
|
||||
setClassLinkCopied(true);
|
||||
setTimeout(() => {
|
||||
setClassLinkCopied(false);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
},
|
||||
classId != "",
|
||||
],
|
||||
].map(
|
||||
(item, i) =>
|
||||
item[3] && (
|
||||
<div
|
||||
key={i}
|
||||
className={
|
||||
"flex gap-2 items-center justify-center transition-colors " +
|
||||
(pickmeMode
|
||||
? "lg:hover:bg-pink-400"
|
||||
: "lg:hover:bg-white lg:hover:text-black") +
|
||||
" p-1 rounded-xl cursor-pointer"
|
||||
}
|
||||
onClick={item[2]}
|
||||
>
|
||||
{item[0]} {item[1]}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
<UserSettings
|
||||
{...{
|
||||
openUserSettings,
|
||||
userFrNames,
|
||||
setUserId,
|
||||
setsetRoute,
|
||||
setRoute,
|
||||
adminId,
|
||||
classId,
|
||||
pickmeMode,
|
||||
}}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
{adminId != "" && (
|
||||
<motion.div
|
||||
layout="position"
|
||||
className="flex w-full justify-end px-5"
|
||||
>
|
||||
<a
|
||||
// onClick={() => {
|
||||
// setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`);
|
||||
// }}
|
||||
href={`#/editor/${apiFormatDate(selectedDate)}`}
|
||||
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"
|
||||
>
|
||||
<BiPencil /> открыть в редакторе
|
||||
</a>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
layout="position"
|
||||
className="flex flex-col gap-5 w-full not-md:px-4"
|
||||
>
|
||||
{adminId != "" && (
|
||||
<motion.div className="flex w-full justify-end">
|
||||
<a
|
||||
// onClick={() => {
|
||||
// setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`);
|
||||
// }}
|
||||
href={`#/editor/${apiFormatDate(selectedDate)}`}
|
||||
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"
|
||||
>
|
||||
<BiPencil /> открыть в редакторе
|
||||
</a>
|
||||
</motion.div>
|
||||
)}
|
||||
<DatePicker
|
||||
date={selectedDate}
|
||||
setDate={setSelectedDate}
|
||||
indicator={typeof rasp[2] == "number" && [1, 2].includes(rasp[2])}
|
||||
/>
|
||||
<RaspView {...{ rasp, pickmeMode, selectedDate }} />
|
||||
<AnimatePresence mode="popLayout">
|
||||
<div className="relative">
|
||||
<LayoutGroup>
|
||||
<RaspView {...{ rasp, pickmeMode, selectedDate }} />
|
||||
</LayoutGroup>
|
||||
{!rasp[0] && (
|
||||
<motion.div
|
||||
className="w-full flex justify-center"
|
||||
initial="hid"
|
||||
animate="show"
|
||||
exit="hid"
|
||||
variants={{
|
||||
show: { opacity: 1, scale: 1 },
|
||||
hid: { opacity: 0, scale: 0 },
|
||||
@@ -341,27 +298,24 @@ export function DX4App({
|
||||
<LoadingSpinner size={25} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
{/* <CoolMouseBg /> */}
|
||||
</motion.div>
|
||||
<CoolMainBg {...{ pickmeMode }} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
function CoolMainBg({ pickmeMode }) {
|
||||
const CoolMainBg = React.memo(({ pickmeMode }) => {
|
||||
return (
|
||||
<div
|
||||
className={`fixed top-0 left-0 w-full h-full -z-5 overflow-hidden bg-gradient-to-br to-black to-50% bg-fixed
|
||||
${pickmeMode ? "from-pink-400/10" : "from-white/2"}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
const [tableColHover, setTableColHover] = useState(-1);
|
||||
});
|
||||
|
||||
export const RaspView = React.memo(({ rasp, pickmeMode, selectedDate }) => {
|
||||
return (
|
||||
<AnimatePresence mode="popLayout">
|
||||
{rasp[0] && typeof rasp[1] == "object" && (
|
||||
@@ -388,48 +342,24 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
className="max-w-10"
|
||||
//layout="position"
|
||||
//layoutId="table-num-h"
|
||||
onMouseEnter={() => {
|
||||
setTableColHover(0);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setTableColHover(-1);
|
||||
}}
|
||||
>
|
||||
n урока
|
||||
</th>
|
||||
<th
|
||||
//layout="position"
|
||||
//layoutId="table-subj-h"
|
||||
onMouseEnter={() => {
|
||||
setTableColHover(1);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setTableColHover(-1);
|
||||
}}
|
||||
//layout="position"
|
||||
//layoutId="table-subj-h"
|
||||
>
|
||||
предмет
|
||||
</th>
|
||||
<th
|
||||
//layout="position"
|
||||
//layoutId="table-cab-h"
|
||||
onMouseEnter={() => {
|
||||
setTableColHover(2);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setTableColHover(-1);
|
||||
}}
|
||||
//layout="position"
|
||||
//layoutId="table-cab-h"
|
||||
>
|
||||
каб
|
||||
</th>
|
||||
<th
|
||||
//layout="position"
|
||||
//layoutId="table-t-h"
|
||||
onMouseEnter={() => {
|
||||
setTableColHover(3);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setTableColHover(-1);
|
||||
}}
|
||||
//layout="position"
|
||||
//layoutId="table-t-h"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
{rasp[2] == 2 && (
|
||||
@@ -449,19 +379,14 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
//rasp-key={"rasp_key_" + i + Array(d).join()}
|
||||
>
|
||||
<td
|
||||
className={
|
||||
(tableColHover == 0 ? "lg:bg-zinc-300/15" : "") +
|
||||
" font-bold"
|
||||
}
|
||||
//layout="position"
|
||||
//layoutId={`table-num-${i}`}
|
||||
//layout="position"
|
||||
//layoutId={`table-num-${i}`}
|
||||
>
|
||||
{d["num"]}
|
||||
</td>
|
||||
<td
|
||||
className={tableColHover == 1 ? "lg:bg-zinc-300/15" : ""}
|
||||
//layout="position"
|
||||
//layoutId={`table-subj-${i}`}
|
||||
//layout="position"
|
||||
//layoutId={`table-subj-${i}`}
|
||||
>
|
||||
{d.subject
|
||||
.toString()
|
||||
@@ -474,9 +399,8 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
)) || d.subject}
|
||||
</td>
|
||||
<td
|
||||
//layout="position"
|
||||
//layoutId={`table-cab-${i}`}
|
||||
className={tableColHover == 2 ? "lg:bg-zinc-300/15" : ""}
|
||||
//layout="position"
|
||||
//layoutId={`table-cab-${i}`}
|
||||
>
|
||||
{d.cabinet
|
||||
.toString()
|
||||
@@ -488,7 +412,7 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
</React.Fragment>
|
||||
)) || d.cabinet}
|
||||
</td>
|
||||
<td className={tableColHover == 3 ? "lg:bg-zinc-300/15" : ""}>
|
||||
<td>
|
||||
<div
|
||||
//layout="position"
|
||||
//layoutId={`table-time-${i}`}
|
||||
@@ -510,18 +434,18 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
{typeof rasp[1] == "number" && rasp[1] === -322 && (
|
||||
<motion.div
|
||||
className="w-full flex justify-center"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, x: 10 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<span className="text-2xl font-bold">нет уроков</span>
|
||||
</motion.div>
|
||||
)}
|
||||
{typeof rasp[1] == "string" && rasp[1].startsWith("ошибка") && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, x: 10 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="w-full flex justify-center"
|
||||
>
|
||||
<span className="text-xl text-center text-transparent bg-clip-text bg-gradient-to-r from-red-300 to-red-500">
|
||||
@@ -531,4 +455,104 @@ export function RaspView({ rasp, pickmeMode, selectedDate }) {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const UserSettings = React.memo(
|
||||
({
|
||||
openUserSettings,
|
||||
userFrNames,
|
||||
setUserId,
|
||||
setsetRoute,
|
||||
setRoute,
|
||||
adminId,
|
||||
classId,
|
||||
pickmeMode,
|
||||
}) => {
|
||||
const [classLinkCopied, setClassLinkCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{openUserSettings && (
|
||||
<motion.div
|
||||
className="flex flex-col bg-grain gap-2 bg-zinc-700/20 p-2 rounded-2xl"
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 10, filter: "blur(2px)" },
|
||||
show: { opacity: 1, y: 0, filter: "blur(0)" },
|
||||
}}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
exit="hidden"
|
||||
>
|
||||
{[
|
||||
// icon
|
||||
// text
|
||||
// onclick
|
||||
// condition
|
||||
[
|
||||
<BiExit />,
|
||||
"выход",
|
||||
() => {
|
||||
setUserId("");
|
||||
setsetRoute(setRoute, "/");
|
||||
},
|
||||
true,
|
||||
],
|
||||
// [
|
||||
// <BiStar />,
|
||||
// "пикми режим",
|
||||
// () => {
|
||||
// setPickmeMode(!pickmeMode);
|
||||
// },
|
||||
// true,
|
||||
// ],
|
||||
[
|
||||
<BiSolidUser />,
|
||||
`админ ${
|
||||
userFrNames["admin"] ? userFrNames["admin"] : adminId
|
||||
}`,
|
||||
() => {},
|
||||
adminId != "",
|
||||
],
|
||||
[
|
||||
<BiLink />,
|
||||
classLinkCopied ? "скопированно" : "коп. ссылку",
|
||||
() => {
|
||||
copyToClipboard(
|
||||
location.href.substring(0, location.href.search("#")) +
|
||||
"#//" +
|
||||
classId
|
||||
).then((a) => {
|
||||
if (a) {
|
||||
setClassLinkCopied(true);
|
||||
setTimeout(() => {
|
||||
setClassLinkCopied(false);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
},
|
||||
classId != "",
|
||||
],
|
||||
].map(
|
||||
(item, i) =>
|
||||
item[3] && (
|
||||
<div
|
||||
key={i}
|
||||
className={
|
||||
"flex gap-2 items-center justify-center transition-colors " +
|
||||
(pickmeMode
|
||||
? "lg:hover:bg-pink-400"
|
||||
: "lg:hover:bg-white lg:hover:text-black") +
|
||||
" p-1 rounded-xl cursor-pointer"
|
||||
}
|
||||
onClick={item[2]}
|
||||
>
|
||||
{item[0]} {item[1]}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BiCheck, BiLeftArrowAlt, BiRightArrowAlt, BiX } from "react-icons/bi";
|
||||
import { formatDate, getDateTags } from "./dengi.js";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { AnimatePresence, motion, LayoutGroup } from "motion/react";
|
||||
|
||||
export function Switch({
|
||||
checked,
|
||||
@@ -105,69 +105,71 @@ function DatePickerButton({ next = false, date, setDate }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function DatePicker(props) {
|
||||
export const DatePicker = React.memo((props) => {
|
||||
const dateTag = getDateTags(props.date);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between px-2 py-1 w-full bg-grain border-zinc-800/40 border rounded-2xl">
|
||||
<DatePickerButton date={props.date} setDate={props.setDate} />
|
||||
<AnimatePresence mode="popLayout">
|
||||
<motion.span
|
||||
key={formatDate(props.date)}
|
||||
className="cursor-pointer flex items-center gap-1 justify-center w-[80%] text-zinc-300 lg:hover:text-black *:transition-colors transition-colors p-1 rounded-3xl lg:hover:bg-white px-3 overflow-hidden lg:hover:*:text-black"
|
||||
variants={{
|
||||
a: { opacity: 0, filter: "blur(1px)" },
|
||||
b: { opacity: 1, filter: "blur(0px)" },
|
||||
}}
|
||||
initial="a"
|
||||
animate="b"
|
||||
exit="a"
|
||||
onClick={() => {
|
||||
props.setDate(new Date());
|
||||
}}
|
||||
>
|
||||
<LayoutGroup>
|
||||
<div className="flex items-center justify-between px-2 py-1 w-full bg-grain border-zinc-800/40 border rounded-2xl relative">
|
||||
<DatePickerButton date={props.date} setDate={props.setDate} />
|
||||
<AnimatePresence mode="popLayout">
|
||||
<motion.span
|
||||
className={
|
||||
"block min-w-2 min-h-2 rounded-full mx-1 transition-colors" +
|
||||
(props.indicator
|
||||
? " bg-zinc-300 animate-pulse"
|
||||
: " bg-transparent")
|
||||
}
|
||||
layoutId="datepicker_indicator"
|
||||
layout="position"
|
||||
/>
|
||||
<motion.span
|
||||
layoutId="datepicker_date"
|
||||
layout="position"
|
||||
className="font-semibold"
|
||||
key={formatDate(props.date)}
|
||||
className="cursor-pointer flex items-center gap-1 justify-center w-[80%] text-zinc-300 lg:hover:text-black *:transition-colors transition-colors p-1 rounded-3xl lg:hover:bg-white px-3 overflow-hidden lg:hover:*:text-black"
|
||||
variants={{
|
||||
a: { opacity: 0, filter: "blur(1px)" },
|
||||
b: { opacity: 1, filter: "blur(0px)" },
|
||||
}}
|
||||
initial="a"
|
||||
animate="b"
|
||||
exit="a"
|
||||
onClick={() => {
|
||||
props.setDate(new Date());
|
||||
}}
|
||||
>
|
||||
{formatDate(props.date)}
|
||||
<motion.span
|
||||
className={
|
||||
"block min-w-2 min-h-2 rounded-full mx-1 transition-colors" +
|
||||
(props.indicator
|
||||
? " bg-zinc-300 animate-pulse"
|
||||
: " bg-transparent")
|
||||
}
|
||||
layoutId="datepicker_indicator"
|
||||
layout="position"
|
||||
/>
|
||||
<motion.span
|
||||
layoutId="datepicker_date"
|
||||
layout="position"
|
||||
className="font-semibold"
|
||||
>
|
||||
{formatDate(props.date)}
|
||||
</motion.span>
|
||||
{dateTag.length > 0 && (
|
||||
<>
|
||||
<motion.div
|
||||
className="flex"
|
||||
layoutId="datepicker_date_div"
|
||||
layout="position"
|
||||
>
|
||||
<span className="h-[1px] -rotate-50 bg-zinc-800 w-[20px] " />
|
||||
</motion.div>
|
||||
<motion.span
|
||||
initial={{ x: 10, opacity: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
className="text-zinc-600 italic"
|
||||
>
|
||||
{dateTag}
|
||||
</motion.span>
|
||||
</>
|
||||
)}
|
||||
</motion.span>
|
||||
{dateTag.length > 0 && (
|
||||
<>
|
||||
<motion.div
|
||||
className="flex"
|
||||
layoutId="datepicker_date_div"
|
||||
layout="position"
|
||||
>
|
||||
<span className="h-[1px] -rotate-50 bg-zinc-800 w-[20px] " />
|
||||
</motion.div>
|
||||
<motion.span
|
||||
initial={{ x: 10, opacity: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
className="text-zinc-600 italic"
|
||||
>
|
||||
{dateTag}
|
||||
</motion.span>
|
||||
</>
|
||||
)}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
<DatePickerButton next date={props.date} setDate={props.setDate} />
|
||||
</div>
|
||||
</AnimatePresence>
|
||||
<DatePickerButton next date={props.date} setDate={props.setDate} />
|
||||
</div>
|
||||
</LayoutGroup>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export function DengiButton({
|
||||
onClick,
|
||||
|
||||
Reference in New Issue
Block a user