diff --git a/pages/admin/index.tsx b/pages/admin/index.tsx index dff1ecf..5f0ffac 100644 --- a/pages/admin/index.tsx +++ b/pages/admin/index.tsx @@ -4,7 +4,7 @@ import useStore from "@/hooks/store"; import Button from "@/library/Button"; import Typography from "@/library/Typography"; import { Input } from "@/library/form"; -import { IEvent } from "@/types/event"; +import { IEvent, IStat } from "@/types/event"; import { patchUserDetails } from "@/utils/api/auth"; import { deleteEvent, getEvents } from "@/utils/api/events"; import { useRouter } from "next/router"; @@ -18,6 +18,7 @@ import { EventCard } from "@/components/admin"; import useDevice from "@/hooks/device"; import styles from "@/styles/pages/admin/Dashboard.module.scss"; import { fetchGlobalStats } from "@/utils/api/stats"; +import { exportAsJSON } from "@/services/files"; const classes = stylesConfig(styles, "admin-dashboard"); @@ -36,17 +37,7 @@ const AdminDashboard: React.FC = () => { avatar: user?.avatar, }); const [events, setEvents] = useState([]); - const [globalStats, setGlobalStats] = useState< - { - event: { - _id: string; - name: string; - teamSize: number; - }; - participants: number; - teams?: number; - }[] - >([]); + const [globalStats, setGlobalStats] = useState([]); const handleChange = (e: React.ChangeEvent) => { setProfileContents((prev) => ({ @@ -103,7 +94,11 @@ const AdminDashboard: React.FC = () => { try { setGettingStats(true); const res = await fetchGlobalStats(); - setGlobalStats(res.data); + setGlobalStats( + res.data.sort((a: IStat, b: IStat) => + a.event.name.localeCompare(b.event.name) + ) + ); } catch (error: any) { console.error(error); toast.error(error?.message ?? "Something went wrong"); @@ -280,6 +275,23 @@ const AdminDashboard: React.FC = () => { > Global Stats + {gettingStats ? ( diff --git a/services/files.ts b/services/files.ts new file mode 100644 index 0000000..26bf765 --- /dev/null +++ b/services/files.ts @@ -0,0 +1,53 @@ +import { FileExtension } from "@/types/files"; + +export const getContentType = (extension: FileExtension) => { + switch (extension) { + case "txt": + return "text/plain"; + case "md": + return "text/markdown"; + case "json": + return "application/json"; + case "csv": + return "text/csv"; + default: + return "text/plain"; + } +}; + +export const saveFile = ( + content: string, + name: string, + extension: FileExtension +) => { + try { + const blob = new Blob([content], { type: getContentType(extension) }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${name}.${extension}`; + link.click(); + window.URL.revokeObjectURL(url); + document.body.removeChild(link); + } catch (error) { + console.error(error); + } +}; + +export const readFile = (file: File) => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + resolve(reader.result as string); + }; + reader.onerror = () => { + reject(reader.error); + }; + reader.readAsText(file); + }); +}; + +export const exportAsJSON = (data: any, name: string) => { + const json = JSON.stringify(data, null, 2); + saveFile(json, name, "json"); +}; diff --git a/styles/pages/admin/Dashboard.module.scss b/styles/pages/admin/Dashboard.module.scss index 2680d99..c10a3af 100644 --- a/styles/pages/admin/Dashboard.module.scss +++ b/styles/pages/admin/Dashboard.module.scss @@ -99,10 +99,6 @@ } &-stats { - &-header { - justify-content: center; - } - table { border-spacing: 1; border-collapse: collapse; diff --git a/types/event.ts b/types/event.ts index a0fa8ae..6327d89 100644 --- a/types/event.ts +++ b/types/event.ts @@ -10,3 +10,13 @@ export interface IEvent { teamSize: number; brochure?: string; } + +export interface IStat { + event: { + _id: string; + name: string; + teamSize: number; + }; + participants: number; + teams?: number; +} diff --git a/types/files.ts b/types/files.ts new file mode 100644 index 0000000..f13cdc1 --- /dev/null +++ b/types/files.ts @@ -0,0 +1 @@ +export type FileExtension = "txt" | "md" | "json" | "csv";