Skip to content

Commit

Permalink
feet: revalidate
Browse files Browse the repository at this point in the history
  • Loading branch information
Rahuletto committed Sep 10, 2024
1 parent 8578c5f commit 9637524
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 67 deletions.
24 changes: 7 additions & 17 deletions app/academia/components/Marks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import Error from "@/components/States/Error";
import Loading from "@/components/States/Loading";
import { useMarks } from "@/provider/MarksProvider";
import dynamic from "next/dynamic";
import { useEffect, useRef, useState } from "react";
import { useRef, useState } from "react";
import { FiInfo } from "react-icons/fi";
import { IoRefreshOutline } from "react-icons/io5";
import NoData from "./subcomponents/NoData";
import { useMutateAll } from "@/hooks/useMutate";

const InfoPopup = dynamic(
() => import("./subcomponents/Attendance/InfoPopup").then((a) => a.default),
Expand All @@ -24,20 +22,10 @@ const MarkCard = dynamic(
);

export default function Marks() {
const mutate = useMutateAll();
const { marks, isLoading, error, requestedAt } = useMarks();
const { marks, isLoading, error, isOld } = useMarks();
const [showInfoPopup, setShowInfoPopup] = useState(false);
const infoIconRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (
!isLoading &&
!error &&
(!requestedAt || Date.now() - requestedAt > 2 * 60 * 60 * 1000)
)
mutate({ mutateMarks: true });
}, [error, isLoading, marks, mutate, requestedAt]);

const toggleInfoPopup = () => setShowInfoPopup((e) => !e);

return (
Expand Down Expand Up @@ -69,14 +57,16 @@ export default function Marks() {
)}
</div>
</div>
{!error && <Refresh type={{ mutateMarks: true }} />}
{!error && <Refresh type={{ mutateMarks: true }} isOld={isOld} />}
</div>
{isLoading ? (
<Loading size="3xl" />
) : error ? (
<Error component="Marks" />
) : marks ? (
<>
<div
className={`${isOld ? "border-light-info-color dark:border-dark-info-color" : "border-transparent"} flex flex-col gap-6 -mx-2 rounded-3xl border-4 border-dotted`}
>
<div className="grid animate-fadeIn grid-cols-marks gap-2 transition-all duration-200">
{marks
?.filter((a) => a.courseType === "Theory")
Expand All @@ -91,7 +81,7 @@ export default function Marks() {
)
.map((mark, i) => <MarkCard key={i} mark={mark} />)}
</div>
</>
</div>
) : (
<NoData component="Marks" />
)}
Expand Down
26 changes: 14 additions & 12 deletions app/academia/components/subcomponents/Attendance/AttendanceCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { AttendanceCourse } from "@/types/Attendance";
import { calculateMargin } from "@/utils/Margin";
import { useTimetable } from "@/provider/TimetableProvider";
Expand All @@ -9,10 +9,9 @@ import AttendancePill from "./AttendancePill";
import Margin from "./Margin";
import Title from "./Title";

const Legend = dynamic(
() => import("./Legend").then((a) => a.default),
{ ssr: true },
);
const Legend = dynamic(() => import("./Legend").then((a) => a.default), {
ssr: true,
});

export default function AttendanceCard({
course,
Expand All @@ -23,6 +22,7 @@ export default function AttendanceCard({
}) {
const { timetable } = useTimetable();
const { day } = useDay();


const {
courseTitle,
Expand Down Expand Up @@ -56,15 +56,17 @@ export default function AttendanceCard({
<div
tabIndex={0}
role="gridcell"
className="my-6 -mx-2 grid w-full grid-cols-[3fr_1fr] grid-rows-[repeat(2,1fr)] items-center gap-3 gap-y-2 rounded-3xl p-4 px-6 transition duration-200 md:my-0 md:flex md:items-center md:justify-between md:rounded-xl"
className="my-6 grid w-full grid-cols-[3fr_1fr] grid-rows-[repeat(2,1fr)] items-center gap-3 gap-y-2 rounded-3xl p-4 px-6 transition duration-200 md:my-0 md:flex md:items-center md:justify-between md:rounded-xl"
>
<Title courseTitle={courseTitle} category={category} />
<Margin
margin={margin}
category={category}
courseTitle={courseTitle}
countHoursPerDay={countHoursPerDay}
/>
<div>
<Margin
margin={margin}
category={category}
courseTitle={courseTitle}
countHoursPerDay={countHoursPerDay}
/>
</div>
<AttendancePill present={present} absent={absent} total={total} />
<span
className={`w-24 self-end justify-self-end text-right font-semibold md:self-center md:justify-self-center ${
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,26 @@
import React, { useEffect } from "react";
import React from "react";
import { useAttendance } from "@/provider/AttendanceProvider";
import Error from "@/components/States/Error";
import Loading from "@/components/States/Loading";
import NoData from "../NoData";
import AttendanceList from "./Predict/AttendanceList";
import { AttendanceCourse } from "@/types/Attendance";
import { useMutateAll } from "@/hooks/useMutate";

export default function AttendanceContent(): JSX.Element {
const { attendance, requestedAt, isLoading, error } = useAttendance();
const mutate = useMutateAll();

useEffect(() => {
if (
!isLoading &&
!error &&
(!requestedAt || Date.now() - requestedAt > 60 * 60 * 1000)
) {
mutate({ mutateAttendance: true });
}
}, [attendance, error, isLoading, mutate, requestedAt]);
const { attendance, isOld, isLoading, error } = useAttendance();

if (isLoading) return <Loading size="max" />;
if (error) return <Error component="Attendance" />;
if (!attendance) return <NoData component="Attendance" />;

return (
<AttendanceList
open={false}
displayedAttendance={attendance as AttendanceCourse[]}
/>
<div
className={`${isOld ? "border-light-info-color dark:border-dark-info-color" : "border-transparent"} -mx-2 rounded-2xl border-4 border-dotted`}
>
<AttendanceList
open={false}
displayedAttendance={attendance as AttendanceCourse[]}
/>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const AttendanceHeader: FC<AttendanceHeaderProps> = ({
const [showInfoPopup, setShowInfoPopup] = useState<boolean>(false);
const infoIconRef = useRef<HTMLDivElement>(null);

const {error} = useAttendance()
const {error, isOld} = useAttendance()

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
Expand Down Expand Up @@ -92,7 +92,7 @@ export const AttendanceHeader: FC<AttendanceHeaderProps> = ({
)}
</div>
</div>
{!error && <Refresh type={{ mutateAttendance: true }} />}
{!error && <Refresh type={{ mutateAttendance: true }} isOld={isOld} />}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const InfoPopup: React.FC<InfoPopupProps> = ({
}) => (
<div
style={{ WebkitBackdropFilter: "blur(10px)" }}
className={`absolute ${bottom ? "top-6" : "bottom-6"} md:-left-20 -left-28 z-10 w-48 animate-fadeIn rounded-xl bg-light-background-light bg-opacity-60 text-light-color shadow-lg backdrop-blur-sm dark:bg-dark-background-light dark:bg-opacity-70 dark:text-dark-color`}
className={`absolute ${bottom ? "top-6" : "bottom-6"} md:-left-20 -left-28 w-48 animate-fadeIn rounded-xl bg-light-background-light bg-opacity-60 text-light-color shadow-lg backdrop-blur-sm dark:bg-dark-background-light dark:bg-opacity-70 dark:text-dark-color`}
>
<p className="p-3 px-4 text-xs font-medium opacity-100 md:text-sm dark:opacity-70">
{text}
Expand Down
52 changes: 44 additions & 8 deletions app/academia/components/subcomponents/Attendance/Legend.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,53 @@
import React from 'react';
import React, { useEffect, useRef, useState } from "react";
import { FiInfo } from "react-icons/fi";
import InfoPopup from "./InfoPopup";

export default function AttendanceLegend() {
const [showInfoPopup, setShowInfoPopup] = useState<boolean>(false);
const infoIconRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
infoIconRef.current &&
!infoIconRef.current.contains(event.target as Node)
) {
setShowInfoPopup(false);
}
};

document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);

const toggleInfoPopup = () => setShowInfoPopup((prev) => !prev);

return (
<div className="hidden items-center justify-between p-4 opacity-40 xl:flex">
<div className="flex w-[300px] items-center space-x-3">
<div className="hidden items-center justify-between p-4 xl:flex">
<div className="flex w-[300px] items-center space-x-3 opacity-40">
<span className="text-xs font-medium text-light-color dark:text-dark-color">
Title
</span>
</div>
<div className="w-24 text-right text-xs">
<span className="text-xs font-medium">Margin</span>
<div className="flex w-24 items-center justify-end gap-3 text-right text-xs">
<span className="text-xs font-medium opacity-40">Margin</span>
<div className="relative" ref={infoIconRef}>
<FiInfo
className="cursor-help opacity-40"
onClick={toggleInfoPopup}
onMouseEnter={toggleInfoPopup}
onMouseLeave={() => setShowInfoPopup(false)}
/>
{showInfoPopup && (
<InfoPopup
warn
text="Enter the dates you'll be absent to see a predicted attendance percentage and margin."
onClose={() => setShowInfoPopup(false)}
/>
)}
</div>
</div>
<div className="flex items-center gap-3 rounded-full bg-light-background-darker p-0.5 text-xs font-medium dark:bg-dark-background-darker">
<div className="flex items-center gap-3 rounded-full bg-light-background-darker p-0.5 text-xs font-medium opacity-40 dark:bg-dark-background-darker">
<div className="flex items-center font-medium">
<span className="rounded-l-full bg-light-success-background px-3 text-light-success-color dark:bg-dark-success-background dark:text-dark-success-color">
Present
Expand All @@ -24,7 +60,7 @@ export default function AttendanceLegend() {
Total
</span>
</div>
<span className="text-xs">Percentage</span>
<span className="text-xs opacity-40">Percentage</span>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ interface AttendanceListProps {
}

export default function AttendanceList({
open,
displayedAttendance,
}: AttendanceListProps) {
return (
<>
{displayedAttendance && (
<div className={`-mx-3 transition duration-200`}>
<div className={`transition duration-200`}>
<AttendanceCard legend course={displayedAttendance[0]} />
</div>
)}
Expand All @@ -31,7 +30,7 @@ export default function AttendanceList({
.map((course, index) => (
<div
key={index}
className="-mx-3 my-1 rounded-xl odd:bg-light-background-normal even:bg-light-background-light odd:dark:bg-dark-background-normal even:dark:bg-transparent"
className="my-1 rounded-xl odd:bg-light-background-normal even:bg-light-background-light odd:dark:bg-dark-background-normal even:dark:bg-transparent"
>
<AttendanceCard course={course} />
</div>
Expand All @@ -42,7 +41,7 @@ export default function AttendanceList({
<Indicator type="Practical" separator />
)}

<div className="my-4">
<div className="mt-4">
{displayedAttendance
?.filter((a) => a.category === "Practical")
.map((course, index) => (
Expand Down
2 changes: 1 addition & 1 deletion components/Indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function Indicator({
separator?: boolean
}) {
return separator ? (
<div className="select-none flex w-full gap-3 items-center" aria-hidden="true" style={{ WebkitUserSelect: "none" }} >
<div className="select-none ml-3 max-w-[97%] flex w-full gap-3 items-center" aria-hidden="true" style={{ WebkitUserSelect: "none" }} >
<span
className={`flex items-start justify-start rounded-full text-xs font-semibold ${type === "Practical" || type === "Lab" ? "dark:text-practical text-light-success-color" : "dark:text-theory text-light-warn-color"}`}
>
Expand Down
6 changes: 3 additions & 3 deletions components/Refresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { IoRefreshOutline } from "react-icons/io5";
import { MutateOptions, useMutateAll } from "@/hooks/useMutate";

export default function Refresh({ type }: { type?: MutateOptions }) {
export default function Refresh({ type, isOld }: { type?: MutateOptions; isOld?: boolean }) {
const mutateAll = useMutateAll();
const clickHandler = () => {
const c = confirm(
Expand All @@ -14,10 +14,10 @@ export default function Refresh({ type }: { type?: MutateOptions }) {
return (
<button
tabIndex={0}
className={`rounded-full p-1 text-sm text-light-color opacity-60 transition duration-200 hover:bg-light-background-dark active:-rotate-45 dark:text-dark-color dark:hover:bg-dark-background-dark`}
className={`rounded-full p-1 text-sm group ${isOld ? "bg-light-info-color dark:bg-dark-info-color dark:text-light-color text-dark-color px-2" : "hover:bg-light-background-dark text-light-color opacity-60 dark:text-dark-color dark:hover:bg-dark-background-dark"}`}
onClick={clickHandler}
>
<IoRefreshOutline />
<IoRefreshOutline className="group-active:-rotate-45 transition duration-200" />
</button>
);
}
3 changes: 3 additions & 0 deletions provider/AttendanceProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ interface AttendanceContextType {
attendance: AttendanceCourse[] | null;
error: Error | null;
requestedAt: number | null;
isOld?: boolean;
isLoading: boolean;
mutate: () => Promise<void | AttendanceResponse | null | undefined>;
}

const AttendanceContext = createContext<AttendanceContextType>({
attendance: null,
error: null,
isOld: false,
requestedAt: null,
isLoading: false,
mutate: async () => {},
Expand Down Expand Up @@ -131,6 +133,7 @@ export function AttendanceProvider({
attendance: attendance?.attendance || null,
requestedAt: attendance?.requestedAt || 0,
error: error || null,
isOld: !isValidating && !error && attendance?.requestedAt ? Date.now() - attendance?.requestedAt > 2 * 60 * 60 * 1000 : false,
isLoading: isValidating,
mutate,
}}
Expand Down
5 changes: 4 additions & 1 deletion provider/MarksProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { token } from "@/utils/Encrypt";

interface MarksContextType {
marks: Mark[] | null;
isOld?: boolean;
requestedAt: number | null;
error: Error | null;
isLoading: boolean;
Expand All @@ -24,6 +25,7 @@ interface MarksContextType {
const MarksContext = createContext<MarksContextType>({
marks: null,
requestedAt: null,
isOld: false,
error: null,
isLoading: false,
mutate: async () => {},
Expand Down Expand Up @@ -133,11 +135,12 @@ export function MarksProvider({
) || null,
requestedAt: marks?.requestedAt || 0,
error: error || null,
isOld: !isValidating && !error && marks?.requestedAt ? Date.now() - marks?.requestedAt > 4 * 60 * 60 * 1000 : false,
isLoading: isValidating,
mutate,
}}
>
{children}
</MarksContext.Provider>
);
}
}

0 comments on commit 9637524

Please sign in to comment.