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 },
+ });
+}