diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6f90d8ce6..37e61c3f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,13 +8,6 @@ repos: args: ["--fix=lf"] - id: trailing-whitespace - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 - hooks: - - id: prettier - types_or: [css, html, javascript, json, jsx, markdown] - exclude: ^frontend/public/assets - - repo: https://github.com/dnephin/pre-commit-golang rev: v0.5.1 hooks: diff --git a/frontend2/.prettierrc.json b/frontend2/.prettierrc.json index 0967ef424..b4bfed357 100644 --- a/frontend2/.prettierrc.json +++ b/frontend2/.prettierrc.json @@ -1 +1,3 @@ -{} +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/frontend2/package-lock.json b/frontend2/package-lock.json index 2d3a9ae40..7a994ba50 100644 --- a/frontend2/package-lock.json +++ b/frontend2/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@headlessui/react": "^1.7.15", + "@headlessui/tailwindcss": "^0.2.0", "@heroicons/react": "^2.0.18", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -37,7 +38,8 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "js-cookie": "^3.0.5", - "prettier": "2.8.8", + "prettier": "^3.0.2", + "prettier-plugin-tailwindcss": "^0.5.3", "react-scripts": "5.0.1", "tailwindcss": "^3.3.2", "typescript": "^4.9.5" @@ -52,7 +54,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "engines": { "node": ">=10" }, @@ -2606,6 +2607,17 @@ "react-dom": "^16 || ^17 || ^18" } }, + "node_modules/@headlessui/tailwindcss": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.0.tgz", + "integrity": "sha512-fpL830Fln1SykOCboExsWr3JIVeQKieLJ3XytLe/tt1A0XzqUthOftDmjcCYLW62w7mQI7wXcoPXr3tZ9QfGxw==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "tailwindcss": "^3.0" + } + }, "node_modules/@heroicons/react": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", @@ -3366,7 +3378,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -3380,7 +3391,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -3389,7 +3399,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -3407,14 +3416,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.18", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -3423,8 +3430,7 @@ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", @@ -3467,7 +3473,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3480,7 +3485,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -3489,7 +3493,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -5541,14 +5544,12 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -5560,8 +5561,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "1.0.10", @@ -6093,8 +6093,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/batch": { "version": "0.6.1", @@ -6130,7 +6129,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -6223,7 +6221,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6233,7 +6230,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -6373,7 +6369,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "engines": { "node": ">= 6" } @@ -6451,7 +6446,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "funding": [ { "type": "individual", @@ -6478,7 +6472,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6704,8 +6697,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/confusing-browser-globals": { "version": "1.0.11", @@ -7114,7 +7106,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -7482,8 +7473,7 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/diff-sequences": { "version": "27.5.1", @@ -7508,8 +7498,7 @@ "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "node_modules/dns-equal": { "version": "1.0.0", @@ -9016,7 +9005,6 @@ "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9032,7 +9020,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -9056,7 +9043,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -9157,7 +9143,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -9513,14 +9498,12 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -9659,7 +9642,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -10277,7 +10259,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -10286,8 +10267,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -10366,7 +10346,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -10404,7 +10383,6 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -10445,7 +10423,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -10472,7 +10449,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -10510,7 +10486,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -12944,7 +12919,6 @@ "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", - "dev": true, "bin": { "jiti": "bin/jiti.js" } @@ -13187,7 +13161,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, "engines": { "node": ">=10" } @@ -13195,8 +13168,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/loader-runner": { "version": "4.3.0", @@ -13399,7 +13371,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -13417,7 +13388,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -13567,7 +13537,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13619,7 +13588,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -13630,7 +13598,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, "funding": [ { "type": "github", @@ -13712,7 +13679,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13772,7 +13738,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -13781,7 +13746,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "engines": { "node": ">= 6" } @@ -13945,7 +13909,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -14141,7 +14104,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -14158,8 +14120,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { "version": "0.1.7", @@ -14185,14 +14146,12 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -14204,7 +14163,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -14213,7 +14171,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, "engines": { "node": ">= 6" } @@ -14359,7 +14316,6 @@ "version": "8.4.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -14780,7 +14736,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -14806,7 +14761,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -14845,7 +14799,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, "dependencies": { "lilconfig": "^2.0.5", "yaml": "^2.1.1" @@ -14874,7 +14827,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true, "engines": { "node": ">= 14" } @@ -15086,7 +15038,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.11" }, @@ -15507,7 +15458,6 @@ "version": "6.0.12", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -15608,8 +15558,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -15621,20 +15570,92 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", + "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.3.tgz", + "integrity": "sha512-M5K80V21yM+CTm/FEFYRv9/9LyInYbCSXpIoPAKMm8zy89IOwdiA2e4JVbcO7tvRtAQWz32zdj7/WKcsmFyAVg==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -15798,7 +15819,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -16185,7 +16205,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -16208,7 +16227,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -16394,7 +16412,6 @@ "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", @@ -16510,7 +16527,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -16610,7 +16626,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -17073,7 +17088,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17439,7 +17453,6 @@ "version": "3.32.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -17461,7 +17474,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "engines": { "node": ">= 6" } @@ -17470,7 +17482,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -17535,7 +17546,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -17646,7 +17656,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", - "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -17826,7 +17835,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -17835,7 +17843,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -17874,7 +17881,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -17936,8 +17942,7 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tsconfig-paths": { "version": "3.14.2", @@ -18228,8 +18233,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/util.promisify": { "version": "1.0.1", @@ -19202,8 +19206,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", diff --git a/frontend2/package.json b/frontend2/package.json index 885718636..19c483fcc 100644 --- a/frontend2/package.json +++ b/frontend2/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@headlessui/react": "^1.7.15", + "@headlessui/tailwindcss": "^0.2.0", "@heroicons/react": "^2.0.18", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -60,7 +61,8 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "js-cookie": "^3.0.5", - "prettier": "2.8.8", + "prettier": "^3.0.2", + "prettier-plugin-tailwindcss": "^0.5.3", "react-scripts": "5.0.1", "tailwindcss": "^3.3.2", "typescript": "^4.9.5" diff --git a/frontend2/src/components/BattlecodeTable.tsx b/frontend2/src/components/BattlecodeTable.tsx index 026df510d..e0ad7e5b2 100644 --- a/frontend2/src/components/BattlecodeTable.tsx +++ b/frontend2/src/components/BattlecodeTable.tsx @@ -26,9 +26,9 @@ function BattlecodeTable({ bottomElement, }: TableProps): React.ReactElement { return ( -
- - +
+
+ {columns.map((col, idx) => ( @@ -74,7 +74,7 @@ function BattlecodeTable({
@@ -48,12 +48,12 @@ function BattlecodeTable({ }} className={ idx % 2 === 0 - ? `bg-white border-b ${ + ? `border-b bg-white ${ onRowClick !== undefined ? "cursor-pointer hover:bg-gray-100 hover:text-gray-700" : "" }}` - : `bg-gray-50 border-b ${ + : `border-b bg-gray-50 ${ onRowClick !== undefined ? "cursor-pointer hover:bg-gray-100 hover:text-gray-700" : "" @@ -64,7 +64,7 @@ function BattlecodeTable({ {col.value(row)}
{loading && ( -
+
)} diff --git a/frontend2/src/components/BattlecodeTableBottomElement.tsx b/frontend2/src/components/BattlecodeTableBottomElement.tsx index 21e6d635d..172d66ef2 100644 --- a/frontend2/src/components/BattlecodeTableBottomElement.tsx +++ b/frontend2/src/components/BattlecodeTableBottomElement.tsx @@ -40,16 +40,16 @@ const BattlecodeTableBottomElement: React.FC = ({ return [1, "..."].concat( Array.from( { length: MAX_PAGES - 2 }, - (_, idx) => pageCount - MAX_PAGES + idx + 3 - ) + (_, idx) => pageCount - MAX_PAGES + idx + 3, + ), ); } else { return [1, "..."] .concat( Array.from( { length: MAX_PAGES - 4 }, - (_, idx) => idx + currentPage - 5 - ) + (_, idx) => idx + currentPage - 5, + ), ) .concat(["...", pageCount]); } @@ -73,7 +73,7 @@ const BattlecodeTableBottomElement: React.FC = ({ {" "} of {totalCount} -
    +
    • { return (
      -
      +
      diff --git a/frontend2/src/components/PrivateRoute.tsx b/frontend2/src/components/PrivateRoute.tsx index d8872dccb..fabec7466 100644 --- a/frontend2/src/components/PrivateRoute.tsx +++ b/frontend2/src/components/PrivateRoute.tsx @@ -13,7 +13,7 @@ const PrivateRoute: React.FC = () => { return null; } else { return ( -
      +
      ); diff --git a/frontend2/src/components/Spinner.tsx b/frontend2/src/components/Spinner.tsx index 4cdb506b5..875477817 100644 --- a/frontend2/src/components/Spinner.tsx +++ b/frontend2/src/components/Spinner.tsx @@ -10,7 +10,7 @@ const Spinner: React.FC = ({ size }) => ( aria-hidden="true" className={`w-${size ?? 16} h-${ size ?? 16 - } mr-2 text-gray-200 animate-spin fill-blue-600`} + } mr-2 animate-spin fill-blue-600 text-gray-200`} // className="w-16 h-16 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" diff --git a/frontend2/src/components/elements/Button.tsx b/frontend2/src/components/elements/Button.tsx index 3f7bb2820..85f910cb8 100644 --- a/frontend2/src/components/elements/Button.tsx +++ b/frontend2/src/components/elements/Button.tsx @@ -1,25 +1,38 @@ import React from "react"; +import Icon, { type IconName } from "./Icon"; interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> { variant?: string; label?: string; + iconName?: IconName; + fullWidth?: boolean; + className?: string; } const variants: Record = { - "": "bg-gray-50 text-gray-900 hover:bg-gray-100 ring-gray-300 ring-1 ring-inset", - dark: "bg-gray-700 text-gray-50 hover:bg-gray-800", + "": "bg-gray-50 text-gray-800 hover:bg-gray-100 hover:ring-gray-900 hover:text-black ring-gray-500 ring-1 ring-inset", + dark: "bg-cyan-700 text-white hover:bg-cyan-800", }; -const Button: React.FC = ({ variant, label, ...rest }) => { - variant = variant ?? ""; - const variantStyle = variants[variant]; +const Button: React.FC = ({ + variant = "", + label, + iconName, + fullWidth = false, + className = "", + ...rest +}) => { + const variantStyle = `${variants[variant]} ${ + fullWidth ? "w-full" : "" + } ${className}`; return ( ); diff --git a/frontend2/src/components/elements/FormError.tsx b/frontend2/src/components/elements/FormError.tsx new file mode 100644 index 000000000..f8c817415 --- /dev/null +++ b/frontend2/src/components/elements/FormError.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +const FormError: React.FC<{ message?: string; className?: string }> = ({ + message, + className, +}) => { + return ( + + {message ?? "This field is invalid."} + + ); +}; + +export default FormError; diff --git a/frontend2/src/components/elements/FormLabel.tsx b/frontend2/src/components/elements/FormLabel.tsx new file mode 100644 index 000000000..1f7b76f97 --- /dev/null +++ b/frontend2/src/components/elements/FormLabel.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const FormLabel: React.FC<{ + label?: string; + required?: boolean; + className?: string; +}> = ({ label, required = false, className }) => { + return ( +
      + {label} + {required && *} +
      + ); +}; + +export default FormLabel; diff --git a/frontend2/src/components/elements/Icon.tsx b/frontend2/src/components/elements/Icon.tsx new file mode 100644 index 000000000..10185113b --- /dev/null +++ b/frontend2/src/components/elements/Icon.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { + ClipboardDocumentIcon as ClipboardDocumentIcon24, + HomeIcon as HomeIcon24, + MapIcon as MapIcon24, + TrophyIcon as TrophyIcon24, + ChartBarIcon as ChartBarIcon24, + ClockIcon as ClockIcon24, + UserGroupIcon as UserGroupIcon24, + ArrowUpTrayIcon as ArrowUpTrayIcon24, + PlayCircleIcon as PlayCircleIcon24, + ChevronDownIcon as ChevronDownIcon24, + CheckIcon as CheckIcon24, + InformationCircleIcon as InformationCircleIcon24, +} from "@heroicons/react/24/outline"; + +import { + ClipboardDocumentIcon as ClipboardDocumentIcon20, + HomeIcon as HomeIcon20, + MapIcon as MapIcon20, + TrophyIcon as TrophyIcon20, + ChartBarIcon as ChartBarIcon20, + ClockIcon as ClockIcon20, + UserGroupIcon as UserGroupIcon20, + ArrowUpTrayIcon as ArrowUpTrayIcon20, + PlayCircleIcon as PlayCircleIcon20, + ChevronDownIcon as ChevronDownIcon20, + CheckIcon as CheckIcon20, + InformationCircleIcon as InformationCircleIcon20, +} from "@heroicons/react/20/solid"; + +const icons24 = { + clipboard_document: ClipboardDocumentIcon24, + home: HomeIcon24, + map: MapIcon24, + trophy: TrophyIcon24, + chart_bar: ChartBarIcon24, + clock: ClockIcon24, + user_group: UserGroupIcon24, + arrow_up_tray: ArrowUpTrayIcon24, + play_circle: PlayCircleIcon24, + chevron_down: ChevronDownIcon24, + check: CheckIcon24, + information_circle: InformationCircleIcon24, +}; + +const icons20 = { + clipboard_document: ClipboardDocumentIcon20, + home: HomeIcon20, + map: MapIcon20, + trophy: TrophyIcon20, + chart_bar: ChartBarIcon20, + clock: ClockIcon20, + user_group: UserGroupIcon20, + arrow_up_tray: ArrowUpTrayIcon20, + play_circle: PlayCircleIcon20, + chevron_down: ChevronDownIcon20, + check: CheckIcon20, + information_circle: InformationCircleIcon20, +}; + +export type IconName = keyof typeof icons24 | keyof typeof icons20; + +export interface IconProps { + name: IconName; + size?: "sm" | "md" | "xs"; + className?: string; +} + +const sizeToClass = { + sm: "h-5 w-5", + md: "h-6 w-6", + xs: "h-4 w-4", +}; + +const Icon: React.FC = ({ + name, + size = "md", + className = "", +}: IconProps) => { + const IconComponent = size === "md" ? icons24[name] : icons20[name]; + return ; +}; + +export default Icon; diff --git a/frontend2/src/components/elements/Input.tsx b/frontend2/src/components/elements/Input.tsx index a34adfcc8..0489d7544 100644 --- a/frontend2/src/components/elements/Input.tsx +++ b/frontend2/src/components/elements/Input.tsx @@ -1,32 +1,36 @@ import React, { forwardRef } from "react"; +import FormError from "./FormError"; +import FormLabel from "./FormLabel"; interface InputProps extends React.ComponentPropsWithoutRef<"input"> { label?: string; required?: boolean; + className?: string; + errorMessage?: string; } const Input = forwardRef(function Input( - { label, required, ...rest }, - ref + { label, required = false, className = "", errorMessage, ...rest }, + ref, ) { - required = required ?? false; + const invalid = errorMessage !== undefined; return ( -
      - {label !== undefined && ( - - )} -
      - -
      +
      +
      ); }); diff --git a/frontend2/src/components/elements/SelectMenu.tsx b/frontend2/src/components/elements/SelectMenu.tsx new file mode 100644 index 000000000..9883aacb1 --- /dev/null +++ b/frontend2/src/components/elements/SelectMenu.tsx @@ -0,0 +1,90 @@ +import React, { Fragment, useMemo } from "react"; +import { Listbox, Transition } from "@headlessui/react"; +import Icon from "./Icon"; +import FormError from "./FormError"; +import FormLabel from "./FormLabel"; + +interface SelectMenuProps { + options: Array<{ value: T; label: string }>; + label?: string; + required?: boolean; + value?: T; + placeholder?: string; + className?: string; + errorMessage?: string; + onChange?: (value: T) => void; +} + +function SelectMenu({ + label, + required = false, + options, + value, + placeholder, + className = "", + errorMessage, + onChange, +}: SelectMenuProps): JSX.Element { + const valueToLabel = useMemo( + () => new Map(options.map((option) => [option.value, option.label])), + [options], + ); + const invalid = errorMessage !== undefined; + return ( +
      + +
      + {label !== undefined && ( + + + + )} + + + {value === undefined ? placeholder : valueToLabel.get(value)} + +
      + +
      +
      + + + {options.map((option) => ( + +
      {option.label}
      + + + +
      + ))} +
      +
      +
      +
      + {invalid && } +
      + ); +} + +export default SelectMenu; diff --git a/frontend2/src/components/sidebar/SidebarItem.tsx b/frontend2/src/components/sidebar/SidebarItem.tsx index 80d5d9ff2..33deb54c5 100644 --- a/frontend2/src/components/sidebar/SidebarItem.tsx +++ b/frontend2/src/components/sidebar/SidebarItem.tsx @@ -1,13 +1,18 @@ import React from "react"; import { NavLink } from "react-router-dom"; +import Icon, { type IconName } from "../elements/Icon"; interface SidebarItemProps { - icon: React.ReactNode; + iconName: IconName; text: string; linkTo: string; } -const SidebarItem: React.FC = ({ icon, text, linkTo }) => { +const SidebarItem: React.FC = ({ + iconName, + text, + linkTo, +}) => { const baseStyle = "text-base flex items-center gap-3 "; const colorVariants = { gray: "text-gray-800 hover:text-gray-400", @@ -20,7 +25,7 @@ const SidebarItem: React.FC = ({ icon, text, linkTo }) => { } to={linkTo} > - {icon} + {text} ); diff --git a/frontend2/src/components/sidebar/SidebarSection.tsx b/frontend2/src/components/sidebar/SidebarSection.tsx index cf3e61642..7394feb7b 100644 --- a/frontend2/src/components/sidebar/SidebarSection.tsx +++ b/frontend2/src/components/sidebar/SidebarSection.tsx @@ -9,7 +9,7 @@ const SidebarSection: React.FC = ({ children, title }) => { return (
      {title !== undefined && ( -

      +

      {title}

      )} diff --git a/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx b/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx index 487590334..c4630b6d8 100644 --- a/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx +++ b/frontend2/src/components/sidebar/__test__/Sidebar.test.tsx @@ -9,14 +9,14 @@ test("UI: should link to default episode", () => { render( - + , ); const linkElement = screen .getByText("Resources") .closest("a") ?.getAttribute("href"); expect(linkElement).toEqual( - expect.stringContaining(`/${DEFAULT_EPISODE}/resources`) + expect.stringContaining(`/${DEFAULT_EPISODE}/resources`), ); }); @@ -24,7 +24,7 @@ test("UI: should collapse sidebar", () => { render( - + , ); expect(screen.queryByText("Home")).toBeNull(); }); @@ -37,7 +37,7 @@ test("UI: should link to episode in surrounding context", () => { > - + , ); const linkElement = screen .getByText("Resources") diff --git a/frontend2/src/components/sidebar/index.tsx b/frontend2/src/components/sidebar/index.tsx index ae25e3862..b50f7d5a0 100644 --- a/frontend2/src/components/sidebar/index.tsx +++ b/frontend2/src/components/sidebar/index.tsx @@ -1,17 +1,6 @@ import React, { useContext } from "react"; import SidebarSection from "./SidebarSection"; import SidebarItem from "./SidebarItem"; -import { - ClipboardDocumentIcon, - HomeIcon, - MapIcon, - TrophyIcon, - ChartBarIcon, - ClockIcon, - UserGroupIcon, - ArrowUpTrayIcon, - PlayCircleIcon, -} from "@heroicons/react/24/outline"; import { EpisodeContext } from "../../contexts/EpisodeContext"; interface SidebarProps { @@ -24,54 +13,50 @@ const Sidebar: React.FC = ({ collapsed }) => { const linkBase = `/${episodeId}/`; return collapsed ? null : ( -
      +
      + } - text="Home" - linkTo={`${linkBase}home`} - /> - } + iconName="map" text="Quick Start" linkTo={`${linkBase}quickstart`} /> } + iconName="clipboard_document" text="Resources" linkTo={`${linkBase}resources`} /> } + iconName="trophy" text="Tournaments" linkTo={`${linkBase}tournaments`} /> } + iconName="chart_bar" text="Rankings" linkTo={`${linkBase}rankings`} /> } + iconName="clock" text="Queue" linkTo={`${linkBase}queue`} /> } + iconName="user_group" text="My Team" linkTo={`${linkBase}team`} /> } + iconName="arrow_up_tray" text="Submissions" linkTo={`${linkBase}submission`} /> } + iconName="play_circle" text="Scrimmaging" linkTo={`${linkBase}scrimmaging`} /> diff --git a/frontend2/src/contexts/CurrentUserContext.ts b/frontend2/src/contexts/CurrentUserContext.ts index 7ab9d51c8..5ce29cdc3 100644 --- a/frontend2/src/contexts/CurrentUserContext.ts +++ b/frontend2/src/contexts/CurrentUserContext.ts @@ -17,7 +17,7 @@ interface CurrentUserContextType { } export const CurrentUserContext = createContext( - null + null, ); export const useCurrentUser = (): CurrentUserContextType => { @@ -25,7 +25,7 @@ export const useCurrentUser = (): CurrentUserContextType => { if (currentUserContext === null) { throw new Error( - "useCurrentUser has to be used within " + "useCurrentUser has to be used within ", ); } diff --git a/frontend2/src/index.css b/frontend2/src/index.css index b5c61c956..601d99fd1 100644 --- a/frontend2/src/index.css +++ b/frontend2/src/index.css @@ -1,3 +1,4 @@ @tailwind base; @tailwind components; @tailwind utilities; +@import url("https://fonts.googleapis.com/css2?family=Inter&family=Josefin+Sans&display=swap"); diff --git a/frontend2/src/index.tsx b/frontend2/src/index.tsx index cfd778490..e36f82832 100644 --- a/frontend2/src/index.tsx +++ b/frontend2/src/index.tsx @@ -4,10 +4,10 @@ import "./index.css"; import App from "./App"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement + document.getElementById("root") as HTMLElement, ); root.render( - + , ); diff --git a/frontend2/src/utils/api.ts b/frontend2/src/utils/api.ts index e932878ef..3bce6153d 100644 --- a/frontend2/src/utils/api.ts +++ b/frontend2/src/utils/api.ts @@ -21,7 +21,7 @@ const API = new ApiApi(baseUrl); * @param credentials The user's credentials. */ export const getApiTokens = async ( - credentials: models.TokenObtainPair + credentials: models.TokenObtainPair, ): Promise => { return (await API.apiTokenCreate(credentials)).body; }; @@ -48,11 +48,11 @@ export const verifyCurrentToken = async (): Promise => { * @param episodeId The current episode's ID. */ export const getAllMaps = async ( - episodeId: string + episodeId: string, ): Promise => { return ( ((await $.get( - `${baseUrl}/api/episode/${episodeId}/map/` + `${baseUrl}/api/episode/${episodeId}/map/`, )) as models.ModelMap[]) ?? [] ); }; @@ -65,7 +65,7 @@ export const getAllMaps = async ( */ export const createTeam = async ( episodeId: string, - teamName: string + teamName: string, ): Promise => { // build default object... why? I couldn't tell you const teamCreate = { @@ -89,7 +89,7 @@ export const createTeam = async ( export const joinTeam = async ( episodeId: string, teamName: string, - joinKey: string + joinKey: string, ): Promise => { const teamInfo = { name: teamName, @@ -113,7 +113,7 @@ export const leaveTeam = async (episodeId: string): Promise => { */ export const updateUserTeamCode = async ( episodeId: string, - joinKey: string + joinKey: string, ): Promise => { return (await API.apiTeamTMePartialUpdate(episodeId, { joinKey })).body; }; @@ -162,7 +162,7 @@ export const searchTeams = async ( episodeId: string, searchQuery: string, requireActiveSubmission: boolean, - page?: number + page?: number, ): Promise => { const apiURL = `${baseUrl}/api/team/${episodeId}/t`; const encQuery = encodeURIComponent(searchQuery); @@ -179,7 +179,7 @@ export const searchTeams = async ( * @param episodeId The current episode's ID. */ export const getEpisodeInfo = async ( - episodeId: string + episodeId: string, ): Promise => { return (await API.apiEpisodeERetrieve(episodeId)).body; }; @@ -212,7 +212,7 @@ export const uploadSubmission = async ( file: File; packageName: string; description: string; - } + }, ): Promise => { const fileData = new FormData(); fileData.append("source_code", submission.file); @@ -235,12 +235,12 @@ export const uploadSubmission = async ( */ export const downloadSubmission = async ( episodeId: string, - submissionId: number + submissionId: number, ): Promise => { const url: string = ( await API.apiCompeteSubmissionDownloadRetrieve( episodeId, - submissionId.toString() + submissionId.toString(), ) ).body.url; @@ -266,7 +266,7 @@ export const downloadSubmission = async ( */ export const getAllSubmissions = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteSubmissionList(episodeId, page)).body; }; @@ -278,12 +278,12 @@ export const getAllSubmissions = async ( */ export const getAllUserTournamentSubmissions = async ( episodeId: string, - page?: number + page?: number, ): Promise => { const res: models.Submission[] = (await $.get( `${baseUrl}/api/compete/${episodeId}/submission/tournament/?page=${ page ?? 1 - }` + }`, )) as unknown as models.Submission[]; return { count: res.length, @@ -298,7 +298,7 @@ export const getAllUserTournamentSubmissions = async ( * @param user The user's info. */ export const createUser = async ( - user: customModels.CreateUserInput + user: customModels.CreateUserInput, ): Promise => { const defaultUser = { id: -1, @@ -329,7 +329,7 @@ export const createUser = async ( * @param userId The user's ID. */ export const getUserProfileByUser = async ( - userId: number + userId: number, ): Promise => { return (await API.apiUserURetrieve(userId)).body; }; @@ -346,7 +346,7 @@ export const getUserUserProfile = async (): Promise => { * @param userId The user's ID. */ export const getTeamsByUser = async ( - userId: number + userId: number, ): Promise => { return (await API.apiUserUTeamsRetrieve(userId)).body; }; @@ -355,7 +355,7 @@ export const getTeamsByUser = async ( * Update the currently logged in user's info. */ export const updateUser = async ( - user: models.PatchedUserPrivate + user: models.PatchedUserPrivate, ): Promise => { await API.apiUserUMePartialUpdate(user); }; @@ -386,7 +386,7 @@ export const avatarUpload = async (avatarFile: File): Promise => { */ export const teamAvatarUpload = async ( episodeId: string, - avatarFile: File + avatarFile: File, ): Promise => { const data = new FormData(); data.append("avatar", avatarFile); @@ -445,7 +445,7 @@ export const downloadResume = async (): Promise => { */ export const uploadUserTeamReport = async ( episodeId: string, - reportFile: File + reportFile: File, ): Promise => { const data = new FormData(); data.append("report", reportFile); @@ -468,7 +468,7 @@ export const uploadUserTeamReport = async ( */ export const acceptScrimmage = async ( episodeId: string, - scrimmageId: number + scrimmageId: number, ): Promise => { const scrimId = scrimmageId.toString(); await API.apiCompeteRequestAcceptCreate(episodeId, scrimId); @@ -481,7 +481,7 @@ export const acceptScrimmage = async ( */ export const rejectScrimmage = async ( episodeId: string, - scrimmageId: number + scrimmageId: number, ): Promise => { const scrimId = scrimmageId.toString(); await API.apiCompeteRequestRejectCreate(episodeId, scrimId); @@ -493,7 +493,7 @@ export const rejectScrimmage = async ( */ export const getUserScrimmagesInbox = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteRequestInboxList(episodeId, page)).body; }; @@ -504,7 +504,7 @@ export const getUserScrimmagesInbox = async ( */ export const getUserScrimmagesOutbox = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteRequestOutboxList(episodeId, page)).body; }; @@ -521,7 +521,7 @@ export const requestScrimmage = async ( requestedTo: number; playerOrder: models.PlayerOrderEnum; mapNames: string[]; - } + }, ): Promise => { // Once again, the important values are params, we can just throw in the rest here to make the type happy const scrimRequest: models.ScrimmageRequest = { @@ -547,7 +547,7 @@ export const requestScrimmage = async ( */ export const getUserScrimmages = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteMatchScrimmageList(episodeId, page)).body; }; @@ -561,7 +561,7 @@ export const getUserScrimmages = async ( export const getScrimmagesByTeam = async ( episodeId: string, teamId: number, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteMatchScrimmageList(episodeId, teamId, page)).body; }; @@ -580,7 +580,7 @@ export const getMatchesByTeam = async ( teamId: number, tournamentId?: string, roundId?: number, - page?: number + page?: number, ): Promise => { return ( await API.apiCompeteMatchTournamentList( @@ -588,7 +588,7 @@ export const getMatchesByTeam = async ( page, roundId, teamId, - tournamentId + tournamentId, ) ).body; }; @@ -600,7 +600,7 @@ export const getMatchesByTeam = async ( */ export const getAllMatches = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteMatchList(episodeId, page)).body; }; @@ -612,7 +612,7 @@ export const getAllMatches = async ( */ export const getAllScrimmages = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteMatchScrimmageList(episodeId, page)).body; }; @@ -624,7 +624,7 @@ export const getAllScrimmages = async ( */ export const getUserMatches = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiCompeteMatchList(episodeId, page)).body; }; @@ -635,7 +635,7 @@ export const getUserMatches = async ( * @param episodeId The current episode's ID. */ export const getNextTournament = async ( - episodeId: string + episodeId: string, ): Promise => { return (await API.apiEpisodeTournamentNextRetrieve(episodeId)).body; }; @@ -647,7 +647,7 @@ export const getNextTournament = async ( */ export const getAllTournaments = async ( episodeId: string, - page?: number + page?: number, ): Promise => { return (await API.apiEpisodeTournamentList(episodeId, page)).body; }; diff --git a/frontend2/src/utils/apiTypes.ts b/frontend2/src/utils/apiTypes.ts index 6667bd6aa..322e9f8cf 100644 --- a/frontend2/src/utils/apiTypes.ts +++ b/frontend2/src/utils/apiTypes.ts @@ -8,259 +8,260 @@ export enum GenderEnum { export type Gender = `${GenderEnum}`; -const countries = [ - "AF", - "AX", - "AL", - "DZ", - "AS", - "AD", - "AO", - "AI", - "AQ", - "AG", - "AR", - "AM", - "AW", - "AU", - "AT", - "AZ", - "BS", - "BH", - "BD", - "BB", - "BY", - "BE", - "BZ", - "BJ", - "BM", - "BT", - "BO", - "BQ", - "BA", - "BW", - "BV", - "BR", - "IO", - "BN", - "BG", - "BF", - "BI", - "CV", - "KH", - "CM", - "CA", - "KY", - "CF", - "TD", - "CL", - "CN", - "CX", - "CC", - "CO", - "KM", - "CG", - "CD", - "CK", - "CR", - "CI", - "HR", - "CU", - "CW", - "CY", - "CZ", - "DK", - "DJ", - "DM", - "DO", - "EC", - "EG", - "SV", - "GQ", - "ER", - "EE", - "SZ", - "ET", - "FK", - "FO", - "FJ", - "FI", - "FR", - "GF", - "PF", - "TF", - "GA", - "GM", - "GE", - "DE", - "GH", - "GI", - "GR", - "GL", - "GD", - "GP", - "GU", - "GT", - "GG", - "GN", - "GW", - "GY", - "HT", - "HM", - "VA", - "HN", - "HK", - "HU", - "IS", - "IN", - "ID", - "IR", - "IQ", - "IE", - "IM", - "IL", - "IT", - "JM", - "JP", - "JE", - "JO", - "KZ", - "KE", - "KI", - "KW", - "KG", - "LA", - "LV", - "LB", - "LS", - "LR", - "LY", - "LI", - "LT", - "LU", - "MO", - "MG", - "MW", - "MY", - "MV", - "ML", - "MT", - "MH", - "MQ", - "MR", - "MU", - "YT", - "MX", - "FM", - "MD", - "MC", - "MN", - "ME", - "MS", - "MA", - "MZ", - "MM", - "NA", - "NR", - "NP", - "NL", - "NC", - "NZ", - "NI", - "NE", - "NG", - "NU", - "NF", - "KP", - "MK", - "MP", - "NO", - "OM", - "PK", - "PW", - "PS", - "PA", - "PG", - "PY", - "PE", - "PH", - "PN", - "PL", - "PT", - "PR", - "QA", - "RE", - "RO", - "RU", - "RW", - "BL", - "SH", - "KN", - "LC", - "MF", - "PM", - "VC", - "WS", - "SM", - "ST", - "SA", - "SN", - "RS", - "SC", - "SL", - "SG", - "SX", - "SK", - "SI", - "SB", - "SO", - "ZA", - "GS", - "KR", - "SS", - "ES", - "LK", - "SD", - "SR", - "SJ", - "SE", - "CH", - "SY", - "TW", - "TJ", - "TZ", - "TH", - "TL", - "TG", - "TK", - "TO", - "TT", - "TN", - "TR", - "TM", - "TC", - "TV", - "UG", - "UA", - "AE", - "GB", - "UM", - "US", - "UY", - "UZ", - "VU", - "VE", - "VN", - "VG", - "VI", - "WF", - "EH", - "YE", - "ZM", - "ZW", -]; +export const COUNTRIES = { + US: "United States of America", + CA: "Canada", + AU: "Australia", + GB: "United Kingdom", + NL: "Netherlands", + KR: "South Korea", + FR: "France", + AF: "Afghanistan", + AX: "Åland Islands", + AL: "Albania", + DZ: "Algeria", + AS: "American Samoa", + AD: "Andorra", + AO: "Angola", + AI: "Anguilla", + AQ: "Antarctica", + AG: "Antigua and Barbuda", + AR: "Argentina", + AM: "Armenia", + AW: "Aruba", + AT: "Austria", + AZ: "Azerbaijan", + BS: "Bahamas", + BH: "Bahrain", + BD: "Bangladesh", + BB: "Barbados", + BY: "Belarus", + BE: "Belgium", + BZ: "Belize", + BJ: "Benin", + BM: "Bermuda", + BT: "Bhutan", + BO: "Bolivia", + BQ: "Bonaire, Sint Eustatius and Saba", + BA: "Bosnia and Herzegovina", + BW: "Botswana", + BV: "Bouvet Island", + BR: "Brazil", + IO: "British Indian Ocean Territory", + BN: "Brunei", + BG: "Bulgaria", + BF: "Burkina Faso", + BI: "Burundi", + CV: "Cabo Verde", + KH: "Cambodia", + CM: "Cameroon", + KY: "Cayman Islands", + CF: "Central African Republic", + TD: "Chad", + CL: "Chile", + CN: "China", + CX: "Christmas Island", + CC: "Cocos (Keeling) Islands", + CO: "Colombia", + KM: "Comoros", + CG: "Congo", + CD: "Congo (the Democratic Republic of the)", + CK: "Cook Islands", + CR: "Costa Rica", + CI: "Côte d'Ivoire", + HR: "Croatia", + CU: "Cuba", + CW: "Curaçao", + CY: "Cyprus", + CZ: "Czechia", + DK: "Denmark", + DJ: "Djibouti", + DM: "Dominica", + DO: "Dominican Republic", + EC: "Ecuador", + EG: "Egypt", + SV: "El Salvador", + GQ: "Equatorial Guinea", + ER: "Eritrea", + EE: "Estonia", + SZ: "Eswatini", + ET: "Ethiopia", + FK: "Falkland Islands (Malvinas)", + FO: "Faroe Islands", + FJ: "Fiji", + FI: "Finland", + GF: "French Guiana", + PF: "French Polynesia", + TF: "French Southern Territories", + GA: "Gabon", + GM: "Gambia", + GE: "Georgia", + DE: "Germany", + GH: "Ghana", + GI: "Gibraltar", + GR: "Greece", + GL: "Greenland", + GD: "Grenada", + GP: "Guadeloupe", + GU: "Guam", + GT: "Guatemala", + GG: "Guernsey", + GN: "Guinea", + GW: "Guinea-Bissau", + GY: "Guyana", + HT: "Haiti", + HM: "Heard Island and McDonald Islands", + VA: "Holy See", + HN: "Honduras", + HK: "Hong Kong", + HU: "Hungary", + IS: "Iceland", + IN: "India", + ID: "Indonesia", + IR: "Iran", + IQ: "Iraq", + IE: "Ireland", + IM: "Isle of Man", + IL: "Israel", + IT: "Italy", + JM: "Jamaica", + JP: "Japan", + JE: "Jersey", + JO: "Jordan", + KZ: "Kazakhstan", + KE: "Kenya", + KI: "Kiribati", + KW: "Kuwait", + KG: "Kyrgyzstan", + LA: "Laos", + LV: "Latvia", + LB: "Lebanon", + LS: "Lesotho", + LR: "Liberia", + LY: "Libya", + LI: "Liechtenstein", + LT: "Lithuania", + LU: "Luxembourg", + MO: "Macao", + MG: "Madagascar", + MW: "Malawi", + MY: "Malaysia", + MV: "Maldives", + ML: "Mali", + MT: "Malta", + MH: "Marshall Islands", + MQ: "Martinique", + MR: "Mauritania", + MU: "Mauritius", + YT: "Mayotte", + MX: "Mexico", + FM: "Micronesia (Federated States of)", + MD: "Moldova", + MC: "Monaco", + MN: "Mongolia", + ME: "Montenegro", + MS: "Montserrat", + MA: "Morocco", + MZ: "Mozambique", + MM: "Myanmar", + NA: "Namibia", + NR: "Nauru", + NP: "Nepal", + NC: "New Caledonia", + NZ: "New Zealand", + NI: "Nicaragua", + NE: "Niger", + NG: "Nigeria", + NU: "Niue", + NF: "Norfolk Island", + KP: "North Korea", + MK: "North Macedonia", + MP: "Northern Mariana Islands", + NO: "Norway", + OM: "Oman", + PK: "Pakistan", + PW: "Palau", + PS: "Palestine, State of", + PA: "Panama", + PG: "Papua New Guinea", + PY: "Paraguay", + PE: "Peru", + PH: "Philippines", + PN: "Pitcairn", + PL: "Poland", + PT: "Portugal", + PR: "Puerto Rico", + QA: "Qatar", + RE: "Réunion", + RO: "Romania", + RU: "Russia", + RW: "Rwanda", + BL: "Saint Barthélemy", + SH: "Saint Helena, Ascension and Tristan da Cunha", + KN: "Saint Kitts and Nevis", + LC: "Saint Lucia", + MF: "Saint Martin (French part)", + PM: "Saint Pierre and Miquelon", + VC: "Saint Vincent and the Grenadines", + WS: "Samoa", + SM: "San Marino", + ST: "Sao Tome and Principe", + SA: "Saudi Arabia", + SN: "Senegal", + RS: "Serbia", + SC: "Seychelles", + SL: "Sierra Leone", + SG: "Singapore", + SX: "Sint Maarten (Dutch part)", + SK: "Slovakia", + SI: "Slovenia", + SB: "Solomon Islands", + SO: "Somalia", + ZA: "South Africa", + GS: "South Georgia and the South Sandwich Islands", + SS: "South Sudan", + ES: "Spain", + LK: "Sri Lanka", + SD: "Sudan", + SR: "Suriname", + SJ: "Svalbard and Jan Mayen", + SE: "Sweden", + CH: "Switzerland", + SY: "Syria", + TW: "Taiwan", + TJ: "Tajikistan", + TZ: "Tanzania", + TH: "Thailand", + TL: "Timor-Leste", + TG: "Togo", + TK: "Tokelau", + TO: "Tonga", + TT: "Trinidad and Tobago", + TN: "Tunisia", + TR: "Turkey", + TM: "Turkmenistan", + TC: "Turks and Caicos Islands", + TV: "Tuvalu", + UG: "Uganda", + UA: "Ukraine", + AE: "United Arab Emirates", + UM: "United States Minor Outlying Islands", + UY: "Uruguay", + UZ: "Uzbekistan", + VU: "Vanuatu", + VE: "Venezuela", + VN: "Vietnam", + VG: "Virgin Islands (British)", + VI: "Virgin Islands (U.S.)", + WF: "Wallis and Futuna", + EH: "Western Sahara", + YE: "Yemen", + ZM: "Zambia", + ZW: "Zimbabwe", +} as const; + +export type Country = keyof typeof COUNTRIES; -export type Country = (typeof countries)[number]; export interface CreateUserInput { profile: { gender: Gender; diff --git a/frontend2/src/utils/auth.ts b/frontend2/src/utils/auth.ts index 429128a53..7c796f6c3 100644 --- a/frontend2/src/utils/auth.ts +++ b/frontend2/src/utils/auth.ts @@ -30,7 +30,7 @@ export const logout = (): void => { */ export const login = async ( username: string, - password: string + password: string, ): Promise => { const credentials = { username, @@ -75,7 +75,7 @@ export const loginCheck = async (): Promise => { * @param user The user to register. */ export const register = async ( - user: customModels.CreateUserInput + user: customModels.CreateUserInput, ): Promise => { const returnedUser = await Api.createUser(user); await login(user.username, user.password); @@ -89,7 +89,7 @@ export const register = async ( */ export const doResetPassword = async ( password: string, - token: string + token: string, ): Promise => { await API.apiUserPasswordResetConfirmCreate({ password, token }); }; diff --git a/frontend2/src/utils/utilTypes.ts b/frontend2/src/utils/utilTypes.ts new file mode 100644 index 000000000..d798e4f97 --- /dev/null +++ b/frontend2/src/utils/utilTypes.ts @@ -0,0 +1 @@ +export type Maybe = T | undefined; diff --git a/frontend2/src/views/Rankings.tsx b/frontend2/src/views/Rankings.tsx index 68fd7e59e..65546cc8f 100644 --- a/frontend2/src/views/Rankings.tsx +++ b/frontend2/src/views/Rankings.tsx @@ -21,7 +21,7 @@ const Rankings: React.FC = () => { const [searchText, setSearchText] = useState(""); const [loading, setLoading] = useState(true); const [data, setData] = useState( - undefined + undefined, ); const [queryParams, setQueryParams] = useSearchParams({ @@ -52,7 +52,7 @@ const Rankings: React.FC = () => { episodeId, searchQuery, false, - page + page, ); setData(result); setLoading(false); @@ -69,11 +69,11 @@ const Rankings: React.FC = () => { }, [searchQuery, page]); return ( -
      +

      Rankings

      -
      +
      { - return

      register page

      ; + const { login } = useCurrentUser(); + const { + register, + handleSubmit, + setValue, + setError, + clearErrors, + formState: { errors }, + } = useForm(); + const [gender, setGender] = useState>(); + const [country, setCountry] = useState>(); + const [formError, setFormError] = useState>(); + + const onSubmit: SubmitHandler = async (data) => { + if (gender === undefined || country === undefined) { + return; + } + try { + const newUser = await Auth.register(data); + login(newUser); + setFormError(undefined); + } catch { + // TODO: display a more helpful error message once the API changes are made + setFormError("Unable to register"); + } + }; + return ( +
      +

      + BATTLECODE +

      + {/* https://github.com/orgs/react-hook-form/discussions/8622 */} +
      { + // validate gender and country + await handleSubmit(onSubmit)(event); + if (gender === undefined) { + setError("profile.gender", { message: REQUIRED_ERROR_MSG }); + } + if (country === undefined) { + setError("profile.country", { message: REQUIRED_ERROR_MSG }); + } + }} + className="m-6 flex w-11/12 flex-col gap-5 rounded-lg bg-gray-100 p-6 shadow-md sm:w-[550px]" + > + { + // TODO: replace this with our custom notification component + formError !== undefined &&

      {formError}

      + } + + + +
      + + +
      + {/* begin profile fields */} +
      + + required + onChange={(newCountry) => { + setCountry(newCountry); + setValue("profile.country", newCountry); + clearErrors("profile.country"); + }} + errorMessage={errors.profile?.country?.message} + value={country} + label="Country" + placeholder="Select country" + options={Object.entries(COUNTRIES).map(([code, name]) => ({ + value: code as Country, + label: name, + }))} + /> + +
      +
      + + required + onChange={(newGender) => { + setGender(newGender); + setValue("profile.gender", newGender); + clearErrors("profile.gender"); + }} + errorMessage={errors.profile?.gender?.message} + value={gender} + label="Gender identity" + placeholder="Select gender" + options={[ + { value: GenderEnum.FEMALE, label: "Female" }, + { value: GenderEnum.MALE, label: "Male" }, + { value: GenderEnum.NONBINARY, label: "Non-binary" }, + { + value: GenderEnum.SELF_DESCRIBED, + label: "Prefer to self describe", + }, + { value: GenderEnum.RATHER_NOT_SAY, label: "Rather not say" }, + ]} + /> + {gender === GenderEnum.SELF_DESCRIBED && ( + + )} +
      + +