diff --git a/docs/yarn.lock b/docs/yarn.lock
index be9a7f7c..a542cf2a 100644
--- a/docs/yarn.lock
+++ b/docs/yarn.lock
@@ -1344,6 +1344,11 @@ picocolors@^1.0.1:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
+picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
pkg-types@^1.0.3, pkg-types@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.2.0.tgz#d0268e894e93acff11a6279de147e83354ebd42d"
@@ -1366,7 +1371,7 @@ points-on-path@^0.2.1:
path-data-parser "0.1.0"
points-on-curve "0.2.0"
-postcss@^8.4.40, postcss@^8.4.41:
+postcss@^8.4.40:
version "8.4.44"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.44.tgz#d56834ef6508610ba224bb22b2457b2169ed0480"
integrity sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==
@@ -1375,6 +1380,15 @@ postcss@^8.4.40, postcss@^8.4.41:
picocolors "^1.0.1"
source-map-js "^1.2.0"
+postcss@^8.4.43:
+ version "8.4.49"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
+ integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
preact@^10.0.0:
version "10.23.2"
resolved "https://registry.yarnpkg.com/preact/-/preact-10.23.2.tgz#52deec92796ae0f0cc6b034d9c66e0fbc1b837dc"
@@ -1449,6 +1463,11 @@ source-map-js@^1.2.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
speakingurl@^14.0.1:
version "14.0.1"
resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53"
@@ -1497,12 +1516,12 @@ uuid@^9.0.1:
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
vite@^5.4.1:
- version "5.4.2"
- resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.2.tgz#8acb6ec4bfab823cdfc1cb2d6c53ed311bc4e47e"
- integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
+ version "5.4.11"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
+ integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==
dependencies:
esbuild "^0.21.3"
- postcss "^8.4.41"
+ postcss "^8.4.43"
rollup "^4.20.0"
optionalDependencies:
fsevents "~2.3.3"
diff --git a/package.json b/package.json
index f291da44..a712c9e1 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"@vitejs/plugin-react-swc": "^3.3",
"acorn": "^8.10",
"blockly": "^11.1",
+ "client-zip": "^2.4",
"clsx": "^2.1",
"commander": "^12.0",
"date-fns": "^3.0",
diff --git a/src/cli/firebase/import.ts b/src/cli/firebase/import.ts
index 38de0c35..237411b9 100644
--- a/src/cli/firebase/import.ts
+++ b/src/cli/firebase/import.ts
@@ -225,6 +225,7 @@ async function importUsers(users: User[], customClaims: object, options: ImportO
let user = await auth.getUserByEmail(record.email).catch(noop);
if (!user) {
user = await auth.createUser({
+ uid: record.id,
email: record.email,
emailVerified: true,
password: record.password,
@@ -305,6 +306,7 @@ async function importVariantMappings(db: Firestore, options: ImportOptions) {
}
const userSchema = z.object({
+ id: z.string().optional(),
name: z.string(),
email: z.string().email(),
password: z.string(),
diff --git a/src/web/firebase/teacher-login.tsx b/src/web/firebase/teacher-login.tsx
index b7f7a90b..b5fc4380 100644
--- a/src/web/firebase/teacher-login.tsx
+++ b/src/web/firebase/teacher-login.tsx
@@ -152,14 +152,23 @@ async function setParticipation(
}
}
-function getPdfStatements(db: Firestore, statementVersion: number, variantIds: string[]) {
+async function getPdfStatements(
+ db: Firestore,
+ statementVersion: number,
+ variantIds: string[],
+): Promise