diff --git a/src/web/admin/dashboard.tsx b/src/web/admin/dashboard.tsx index 8b8a2a46..3dab6e87 100644 --- a/src/web/admin/dashboard.tsx +++ b/src/web/admin/dashboard.tsx @@ -1,6 +1,7 @@ import { Suspense } from "react"; import { Card, CardActions, CardBody } from "@olinfo/react-components"; +import { Link } from "wouter"; import type { Participation, Student } from "~/models"; import { Loading } from "~/web/components"; @@ -29,12 +30,15 @@ export default function Dashboard() { - -
- }> - - -
+ + }> + + + + + Gestione scuole + +
{contest.hasOnline && ( diff --git a/src/web/admin/provider.tsx b/src/web/admin/provider.tsx index 9b447135..5ac4c24d 100644 --- a/src/web/admin/provider.tsx +++ b/src/web/admin/provider.tsx @@ -1,10 +1,11 @@ import { createContext, lazy, useContext } from "react"; -import { Link, Redirect, Route, useParams } from "wouter"; +import { Link, Redirect, Route, Switch, useParams } from "wouter"; import type { Contest } from "~/models"; import { AdminLayout } from "./layout"; +import { SchoolTable } from "./school-table"; type AdminContextProps = { name: string; @@ -57,7 +58,14 @@ function ProviderInner({ contests, ...props }: AdminProviderProps) { return ( - + + + + + + + + ); } diff --git a/src/web/admin/school-table.tsx b/src/web/admin/school-table.tsx new file mode 100644 index 00000000..64bb7736 --- /dev/null +++ b/src/web/admin/school-table.tsx @@ -0,0 +1,107 @@ +import { type ComponentType, Suspense, lazy, useMemo } from "react"; + +import type { CellEditRequestEvent, ColDef, ICellRendererParams } from "@ag-grid-community/core"; +import { AG_GRID_LOCALE_IT } from "@ag-grid-community/locale"; +import type { AgGridReactProps } from "@ag-grid-community/react/dist/types/src/shared/interfaces"; + +import type { Participation, Student } from "~/models"; +import { Loading } from "~/web/components"; +import { participationConverter } from "~/web/firebase/common/converters"; +import { useCollection } from "~/web/firebase/hooks"; + +import { useAdmin } from "./provider"; + +import "@ag-grid-community/styles/ag-grid.css"; +import "@ag-grid-community/styles/ag-theme-quartz.css"; +import { useCount } from "~/web/firebase/hooks/count"; + +const AgGridReact: ComponentType = lazy(() => import("~/web/components/ag-grid")); + +export function SchoolTable() { + const { contest } = useAdmin(); + + const [participations, setParticipation] = useCollection( + "participations", + participationConverter, + { + constraints: { contestId: contest.id }, + subscribe: true, + }, + ); + + const colDefs = useMemo(() => columnDefinition(), []); + + const onCellEditRequest = async (ev: CellEditRequestEvent) => { + const participation = ev.data as Participation; + if (ev.colDef.field === "finalized") { + await setParticipation({ ...participation, finalized: ev.newValue }); + } + ev.api.refreshCells({ force: true }); + }; + + return ( + }> +
+
+ (row.data as Participation).id} + columnDefs={colDefs} + singleClickEdit={true} + readOnlyEdit={true} + rowSelection="single" + onCellEditRequest={onCellEditRequest} + enableBrowserTooltips={true} + localeText={AG_GRID_LOCALE_IT} + /> +
+
+
+ ); +} + +function columnDefinition(): ColDef[] { + return [ + { + field: "schoolId", + headerName: "ID", + width: 150, + filter: true, + }, + { + field: "name", + headerName: "Nome", + minWidth: 200, + flex: 1, + filter: true, + }, + { + field: "count", + headerName: "Studenti", + width: 100, + sortable: false, + cellRenderer: ({ data }: ICellRendererParams) => { + if (!data) return; + return ( + + + + ); + }, + }, + { + field: "finalized", + headerName: "Finalizzato", + width: 100, + filter: true, + cellDataType: "boolean", + editable: true, + }, + ]; +} + +function Count({ participationId }: { participationId: string }) { + return useCount(`participations/${participationId}/students`, { + constraints: { disabled: false }, + }); +}