[web] Refactor team page and add lang choice
This commit is contained in:
parent
9317974df7
commit
80804734a5
@ -29,17 +29,20 @@ node build
|
||||
### Docker
|
||||
|
||||
Copy the example docker compose file
|
||||
|
||||
```bash
|
||||
# pwd web/
|
||||
cp docker/docker-compose.example.yml ./docker-compose.yml
|
||||
```
|
||||
|
||||
Fill out `.env` file
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Run the container
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
252
web/package-lock.json
generated
252
web/package-lock.json
generated
@ -10,13 +10,13 @@
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.5.2",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@types/fs-extra": "^11.0.3",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.1",
|
||||
"diff": "^5.1.0",
|
||||
"diff2html": "^3.4.45",
|
||||
"eslint-plugin-svelte": "^2.34.1",
|
||||
"eslint-plugin-svelte": "^2.35.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"node-git-server": "^1.0.0",
|
||||
@ -27,22 +27,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.1.1",
|
||||
"@sveltejs/kit": "^1.27.3",
|
||||
"@types/bcrypt": "^5.0.1",
|
||||
"@types/bootstrap": "^5.2.8",
|
||||
"@types/diff": "^5.0.7",
|
||||
"@types/js-cookie": "^3.0.5",
|
||||
"@types/node": "^20.8.10",
|
||||
"@types/uuid": "^9.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||
"@typescript-eslint/parser": "^6.9.1",
|
||||
"@sveltejs/kit": "^1.27.5",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/bootstrap": "^5.2.9",
|
||||
"@types/diff": "^5.0.8",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-svelte": "^3.0.3",
|
||||
"prettier-plugin-svelte": "^3.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"svelte": "^4.2.2",
|
||||
"svelte-check": "^3.5.2",
|
||||
"svelte": "^4.2.3",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0"
|
||||
@ -822,12 +822,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "1.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.27.3.tgz",
|
||||
"integrity": "sha512-pd7qwX6ww5noA0/FLk45B0aKUeOXWR+pfZsGTrv3dRmj3lTmnki9UTmTdWzHJGrje+BBkGUZHfgGrsSOQQBQpQ==",
|
||||
"version": "1.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.27.5.tgz",
|
||||
"integrity": "sha512-+L1WPs/ZYNjXoBFoFARypD4aZOjkT51vFpRCtQI45+Fmmfi4Y0dH/8VFlmYD6VlGe89ViIPg7lgf/JpGQ2tr7A==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.5.0",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"cookie": "^0.5.0",
|
||||
"devalue": "^4.3.1",
|
||||
@ -848,20 +848,20 @@
|
||||
"node": "^16.14 || >=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.54.0 || ^4.0.0-next.0",
|
||||
"svelte": "^3.54.0 || ^4.0.0-next.0 || ^5.0.0-next.0",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",
|
||||
"integrity": "sha512-UJKsFNwhzCVuiZd06jM/psscyNJNDwjQC+qIeb7GBJK9iWeQCcIyfcPWDvbCudfcJggY9jtxJeeaZH7uny93FQ==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.2.tgz",
|
||||
"integrity": "sha512-Dfy0Rbl+IctOVfJvWGxrX/3m6vxPLH8o0x+8FA5QEyMUQMo4kGOVIojjryU7YomBAexOTAuYf1RT7809yDziaA==",
|
||||
"dependencies": {
|
||||
"@sveltejs/vite-plugin-svelte-inspector": "^1.0.3",
|
||||
"@sveltejs/vite-plugin-svelte-inspector": "^1.0.4",
|
||||
"debug": "^4.3.4",
|
||||
"deepmerge": "^4.3.1",
|
||||
"kleur": "^4.1.5",
|
||||
"magic-string": "^0.30.2",
|
||||
"magic-string": "^0.30.3",
|
||||
"svelte-hmr": "^0.15.3",
|
||||
"vitefu": "^0.2.4"
|
||||
},
|
||||
@ -869,7 +869,7 @@
|
||||
"node": "^14.18.0 || >= 16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.54.0 || ^4.0.0",
|
||||
"svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
},
|
||||
@ -890,18 +890,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bcrypt": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.1.tgz",
|
||||
"integrity": "sha512-dIIrEsLV1/v0AUNI8oHMaRRTSeVjoy5ID8oclJavtPj8CwPJoD1eFoNXEypuu6k091brEzBeOo3LlxeAH9zRZg==",
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz",
|
||||
"integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bootstrap": {
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.8.tgz",
|
||||
"integrity": "sha512-14do+aWZPc1w3G+YevSsy8eas1XEPhTOUNBhQX/r12YKn7ySssATJusBQ/HCQAd2nq54U8vvrftHSb1YpeJUXg==",
|
||||
"version": "5.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.9.tgz",
|
||||
"integrity": "sha512-Fcg4nORBKaVUAG4F0ePWcatWQVfr3NAT9XIN+hl1PaiAwb4tq55+iua9R3exsbB3yyfhyQlHYg2foTlW86J+RA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.2"
|
||||
@ -913,9 +913,9 @@
|
||||
"integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g=="
|
||||
},
|
||||
"node_modules/@types/diff": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.7.tgz",
|
||||
"integrity": "sha512-adBosR2GntaQQiuHnfRN9HtxYpoHHJBcdyz7VSXhjpSAmtvIfu/S1fjTqwuIx/Ypba6LCZdfWIqPYx2BR5TneQ==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.8.tgz",
|
||||
"integrity": "sha512-kR0gRf0wMwpxQq6ME5s+tWk9zVCfJUl98eRkD05HWWRbhPB/eu4V1IbyZAsvzC1Gn4znBJ0HN01M4DGXdBEV8Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
@ -924,24 +924,24 @@
|
||||
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.3.tgz",
|
||||
"integrity": "sha512-sF59BlXtUdzEAL1u0MSvuzWd7PdZvZEtnaVkzX5mjpdWTJ8brG0jUqve3jPCzSzvAKKMHTG8F8o/WMQLtleZdQ==",
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
|
||||
"integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==",
|
||||
"dependencies": {
|
||||
"@types/jsonfile": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/js-cookie": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||
"integrity": "sha512-dtLshqoiGRDHbHueIT9sjkd2F4tW1qPSX2xKAQK8p1e6pM+Z913GM1shv7dOqqasEMYbC5zEaClJomQe8OtQLA==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
|
||||
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz",
|
||||
"integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==",
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/jsonfile": {
|
||||
@ -953,17 +953,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.8.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz",
|
||||
"integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==",
|
||||
"version": "20.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
|
||||
"integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pug": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz",
|
||||
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.9.tgz",
|
||||
"integrity": "sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
@ -972,28 +972,28 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==",
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz",
|
||||
"integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz",
|
||||
"integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==",
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz",
|
||||
"integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz",
|
||||
"integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz",
|
||||
"integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.9.1",
|
||||
"@typescript-eslint/type-utils": "6.9.1",
|
||||
"@typescript-eslint/utils": "6.9.1",
|
||||
"@typescript-eslint/visitor-keys": "6.9.1",
|
||||
"@typescript-eslint/scope-manager": "6.10.0",
|
||||
"@typescript-eslint/type-utils": "6.10.0",
|
||||
"@typescript-eslint/utils": "6.10.0",
|
||||
"@typescript-eslint/visitor-keys": "6.10.0",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
@ -1019,15 +1019,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz",
|
||||
"integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz",
|
||||
"integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.9.1",
|
||||
"@typescript-eslint/types": "6.9.1",
|
||||
"@typescript-eslint/typescript-estree": "6.9.1",
|
||||
"@typescript-eslint/visitor-keys": "6.9.1",
|
||||
"@typescript-eslint/scope-manager": "6.10.0",
|
||||
"@typescript-eslint/types": "6.10.0",
|
||||
"@typescript-eslint/typescript-estree": "6.10.0",
|
||||
"@typescript-eslint/visitor-keys": "6.10.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1047,13 +1047,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz",
|
||||
"integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz",
|
||||
"integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.9.1",
|
||||
"@typescript-eslint/visitor-keys": "6.9.1"
|
||||
"@typescript-eslint/types": "6.10.0",
|
||||
"@typescript-eslint/visitor-keys": "6.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
@ -1064,13 +1064,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz",
|
||||
"integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz",
|
||||
"integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "6.9.1",
|
||||
"@typescript-eslint/utils": "6.9.1",
|
||||
"@typescript-eslint/typescript-estree": "6.10.0",
|
||||
"@typescript-eslint/utils": "6.10.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
@ -1091,9 +1091,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
|
||||
"integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz",
|
||||
"integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
@ -1104,13 +1104,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz",
|
||||
"integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz",
|
||||
"integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.9.1",
|
||||
"@typescript-eslint/visitor-keys": "6.9.1",
|
||||
"@typescript-eslint/types": "6.10.0",
|
||||
"@typescript-eslint/visitor-keys": "6.10.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1131,17 +1131,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz",
|
||||
"integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz",
|
||||
"integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.9.1",
|
||||
"@typescript-eslint/types": "6.9.1",
|
||||
"@typescript-eslint/typescript-estree": "6.9.1",
|
||||
"@typescript-eslint/scope-manager": "6.10.0",
|
||||
"@typescript-eslint/types": "6.10.0",
|
||||
"@typescript-eslint/typescript-estree": "6.10.0",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1156,12 +1156,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
|
||||
"integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz",
|
||||
"integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.9.1",
|
||||
"@typescript-eslint/types": "6.10.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -1805,6 +1805,17 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-compat-utils": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz",
|
||||
"integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
|
||||
@ -1818,13 +1829,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-svelte": {
|
||||
"version": "2.34.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.34.1.tgz",
|
||||
"integrity": "sha512-HnLzYevh9bLL0Rj2d4dmZY9EutN0BL5JsJRHqtJFIyaEmdxxd3ZuY5zNoSjIFhctFMSntsClbd6TwYjgaOY0Xw==",
|
||||
"version": "2.35.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.35.0.tgz",
|
||||
"integrity": "sha512-3WDFxNrkXaMlpqoNo3M1ZOQuoFLMO9+bdnN6oVVXaydXC7nzCJuGy9a0zqoNDHMSRPYt0Rqo6hIdHMEaI5sQnw==",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14",
|
||||
"debug": "^4.3.1",
|
||||
"eslint-compat-utils": "^0.1.2",
|
||||
"esutils": "^2.0.3",
|
||||
"known-css-properties": "^0.29.0",
|
||||
"postcss": "^8.4.5",
|
||||
@ -3065,13 +3077,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-svelte": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.0.3.tgz",
|
||||
"integrity": "sha512-dLhieh4obJEK1hnZ6koxF+tMUrZbV5YGvRpf2+OADyanjya5j0z1Llo8iGwiHmFWZVG/hLEw/AJD5chXd9r3XA==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.0.tgz",
|
||||
"integrity": "sha512-96+AZxs2ESqIFA9j+o+DHqY+BsUglezfl553LQd6VOtTyJq5GPuBEb3ElxF2cerFzKlYKttlH/VcVmRNj5oc3A==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"prettier": "^3.0.0",
|
||||
"svelte": "^3.2.0 || ^4.0.0-next.0"
|
||||
"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
@ -3481,9 +3493,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz",
|
||||
"integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.3.tgz",
|
||||
"integrity": "sha512-sqmG9KC6uUc7fb3ZuWoxXvqk6MI9Uu4ABA1M0fYDgTlFYu1k02xp96u6U9+yJZiVm84m9zge7rrA/BNZdFpOKw==",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
@ -3504,9 +3516,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-check": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.5.2.tgz",
|
||||
"integrity": "sha512-5a/YWbiH4c+AqAUP+0VneiV5bP8YOk9JL3jwvN+k2PEPLgpu85bjQc5eE67+eIZBBwUEJzmO3I92OqKcqbp3fw==",
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.0.tgz",
|
||||
"integrity": "sha512-8VfqhfuRJ1sKW+o8isH2kPi0RhjXH1nNsIbCFGyoUHG+ZxVxHYRKcb+S8eaL/1tyj3VGvWYx3Y5+oCUsJgnzcw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
@ -3515,14 +3527,14 @@
|
||||
"import-fresh": "^3.2.1",
|
||||
"picocolors": "^1.0.0",
|
||||
"sade": "^1.7.4",
|
||||
"svelte-preprocess": "^5.0.4",
|
||||
"svelte-preprocess": "^5.1.0",
|
||||
"typescript": "^5.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"svelte-check": "bin/svelte-check"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0"
|
||||
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-eslint-parser": {
|
||||
@ -3563,9 +3575,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-preprocess": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz",
|
||||
"integrity": "sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.0.tgz",
|
||||
"integrity": "sha512-EkErPiDzHAc0k2MF5m6vBNmRUh338h2myhinUw/xaqsLs7/ZvsgREiLGj03VrSzbY/TB5ZXgBOsKraFee5yceA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
@ -3588,7 +3600,7 @@
|
||||
"sass": "^1.26.8",
|
||||
"stylus": "^0.55.0",
|
||||
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
||||
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0",
|
||||
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
|
||||
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
@ -3880,11 +3892,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitefu": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
|
||||
"integrity": "sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==",
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
|
||||
"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
|
||||
"peerDependencies": {
|
||||
"vite": "^3.0.0 || ^4.0.0"
|
||||
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"vite": {
|
||||
|
@ -13,22 +13,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.1.1",
|
||||
"@sveltejs/kit": "^1.27.3",
|
||||
"@types/bcrypt": "^5.0.1",
|
||||
"@types/bootstrap": "^5.2.8",
|
||||
"@types/diff": "^5.0.7",
|
||||
"@types/js-cookie": "^3.0.5",
|
||||
"@types/node": "^20.8.10",
|
||||
"@types/uuid": "^9.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||
"@typescript-eslint/parser": "^6.9.1",
|
||||
"@sveltejs/kit": "^1.27.5",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/bootstrap": "^5.2.9",
|
||||
"@types/diff": "^5.0.8",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-svelte": "^3.0.3",
|
||||
"prettier-plugin-svelte": "^3.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"svelte": "^4.2.2",
|
||||
"svelte-check": "^3.5.2",
|
||||
"svelte": "^4.2.3",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0"
|
||||
@ -37,13 +37,13 @@
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.5.2",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@types/fs-extra": "^11.0.3",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.1",
|
||||
"diff": "^5.1.0",
|
||||
"diff2html": "^3.4.45",
|
||||
"eslint-plugin-svelte": "^2.34.1",
|
||||
"eslint-plugin-svelte": "^2.35.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"node-git-server": "^1.0.0",
|
||||
|
@ -65,6 +65,12 @@ model Team {
|
||||
contests Contest[] @relation("TeamContestRelation")
|
||||
password String
|
||||
activeTeam ActiveTeam?
|
||||
language Language
|
||||
}
|
||||
|
||||
enum Language {
|
||||
Java
|
||||
CSharp
|
||||
}
|
||||
|
||||
model ActiveTeam {
|
||||
|
48
web/src/lib/Modal.svelte
Normal file
48
web/src/lib/Modal.svelte
Normal file
@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type bootstrap from 'bootstrap';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
|
||||
export let title: string;
|
||||
export let closeButton = true;
|
||||
|
||||
let modalElement: HTMLDivElement;
|
||||
let modal: bootstrap.Modal | undefined;
|
||||
|
||||
export function show() {
|
||||
modal?.show();
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
modal?.hide();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const bootstrap = await import('bootstrap');
|
||||
modal = new bootstrap.Modal(modalElement);
|
||||
});
|
||||
|
||||
beforeNavigate(() => {
|
||||
modal?.hide();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={modalElement} class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title fs-5">{title}</h2>
|
||||
{#if closeButton}
|
||||
<button
|
||||
on:click={() => {
|
||||
modal?.hide();
|
||||
}}
|
||||
type="button"
|
||||
class="btn-close"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -3,7 +3,7 @@
|
||||
import { theme } from '../stores';
|
||||
</script>
|
||||
|
||||
<nav class="main-nav mt-2 mb-3 navbar navbar-expand-lg bg-body-secondary">
|
||||
<nav class="main-nav mt-2 mb-3 navbar navbar-expand-lg bg-body-secondary shadow-sm">
|
||||
<div class="container-fluid">
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
@ -21,6 +21,9 @@
|
||||
<li class="nav-item">
|
||||
<a href="/admin" class="nav-link"><i class="bi bi-speedometer2"></i> Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/teams" class="nav-link"><i class="bi bi-people"></i> Teams</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/reviews" class="nav-link"><i class="bi bi-eye"></i> Reviews</a>
|
||||
</li>
|
||||
@ -37,9 +40,6 @@
|
||||
<li class="nav-item">
|
||||
<a href="/admin/scoreboard" class="nav-link"><i class="bi bi-trophy"></i> Scoreboards</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/teams" class="nav-link"><i class="bi bi-people"></i> Teams</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/contests" class="nav-link"><i class="bi bi-flag"></i> Contests</a>
|
||||
</li>
|
||||
|
@ -3,25 +3,30 @@ import type { Actions, PageServerLoad } from './$types';
|
||||
import { genPassword } from './util';
|
||||
|
||||
export const load = (async () => {
|
||||
const teams = await db.team.findMany();
|
||||
return {
|
||||
teams: teams.map((row) => {
|
||||
return { id: row.id, name: row.name };
|
||||
})
|
||||
};
|
||||
const teams = await db.team.findMany({
|
||||
select: { id: true, name: true, language: true, password: true },
|
||||
orderBy: { name: 'asc' }
|
||||
});
|
||||
return { teams };
|
||||
}) satisfies PageServerLoad;
|
||||
|
||||
export const actions = {
|
||||
add: async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
const name = data.get('name');
|
||||
if (!name) {
|
||||
return { success: false };
|
||||
const lang = data.get('lang');
|
||||
if (name === null || lang === null) {
|
||||
return { success: false, message: 'Incomplete form data' };
|
||||
}
|
||||
if (lang !== 'Java' && lang !== 'CSharp') {
|
||||
return { success: false, message: 'Invalid language' };
|
||||
}
|
||||
try {
|
||||
await db.team.create({ data: { name: name.toString(), password: genPassword() } });
|
||||
await db.team.create({
|
||||
data: { name: name.toString(), password: genPassword(), language: lang }
|
||||
});
|
||||
} catch {
|
||||
return { success: false };
|
||||
return { success: false, message: 'Database error' };
|
||||
}
|
||||
return { success: true };
|
||||
},
|
||||
@ -38,5 +43,28 @@ export const actions = {
|
||||
return { success: false };
|
||||
}
|
||||
return { success: true };
|
||||
},
|
||||
edit: async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
const teamId = data.get('id');
|
||||
const name = data.get('name');
|
||||
const lang = data.get('lang');
|
||||
const password = data.get('password');
|
||||
if (teamId === null || name === null || lang === null || password === null) {
|
||||
return { success: false, message: 'Incomplete form data' };
|
||||
}
|
||||
if (lang !== 'Java' && lang !== 'CSharp') {
|
||||
return { success: false, message: 'Invalid language' };
|
||||
}
|
||||
try {
|
||||
await db.team.update({
|
||||
where: { id: parseInt(teamId.toString()) },
|
||||
data: { name: name.toString(), language: lang, password: password.toString() }
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return { success: false, message: 'Database error' };
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
} satisfies Actions;
|
||||
|
@ -1,66 +1,149 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
import FormAlert from '$lib/FormAlert.svelte';
|
||||
import Modal from '$lib/Modal.svelte';
|
||||
import type { Actions, PageData } from './$types';
|
||||
import { genPassword } from './util';
|
||||
|
||||
export let data: PageData;
|
||||
export let form: Actions;
|
||||
|
||||
let adding = false;
|
||||
|
||||
$: if (form && form.success) {
|
||||
adding = false;
|
||||
function editGenPassword() {
|
||||
(document.getElementById('editTeamPassword') as HTMLInputElement).value = genPassword();
|
||||
}
|
||||
|
||||
$: if (form) {
|
||||
addModal.hide();
|
||||
editModal.hide();
|
||||
confirmModal.cancel();
|
||||
}
|
||||
|
||||
let addModal: Modal;
|
||||
let confirmModal: ConfirmModal;
|
||||
let editModal: Modal;
|
||||
|
||||
let editTeam: PageData['teams'][number] | undefined;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Teams</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-people"></i> Teams</h1>
|
||||
<FormAlert />
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">Invalid action</div>
|
||||
{/if}
|
||||
<Modal title="Edit Team" bind:this={editModal}>
|
||||
<form
|
||||
action="?/edit"
|
||||
method="POST"
|
||||
use:enhance={() => {
|
||||
return async ({ update }) => {
|
||||
await update({ reset: false });
|
||||
};
|
||||
}}
|
||||
>
|
||||
<div class="modal-body">
|
||||
{#if editTeam !== undefined}
|
||||
<input type="hidden" name="id" value={editTeam.id} />
|
||||
|
||||
<label class="form-label" for="editTeamName">Name</label>
|
||||
<input
|
||||
name="name"
|
||||
id="editTeamName"
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={editTeam.name}
|
||||
required
|
||||
/>
|
||||
|
||||
<label class="mt-1 form-label" for="editTeamLang">Language</label>
|
||||
<select
|
||||
id="editTeamLang"
|
||||
name="lang"
|
||||
class="form-select"
|
||||
value={editTeam.language}
|
||||
required
|
||||
>
|
||||
<option value="Java">Java</option>
|
||||
<option value="CSharp">C#</option>
|
||||
</select>
|
||||
|
||||
<label class="mt-1 form-label" for="editTeamPassword">Password</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
name="password"
|
||||
id="editTeamPassword"
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={editTeam.password}
|
||||
required
|
||||
/>
|
||||
<button on:click={editGenPassword} class="btn btn-outline-primary"
|
||||
><i class="bi bi-arrow-clockwise"></i></button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
on:click={() => {
|
||||
editModal.hide();
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-secondary">Cancel</button
|
||||
>
|
||||
<button type="submit" class="btn btn-warning">Submit Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
<Modal title="Add Team" bind:this={addModal}>
|
||||
<form action="?/add" method="POST" use:enhance>
|
||||
<div class="modal-body">
|
||||
<label class="form-label" for="addTeamName">Name</label>
|
||||
<input name="name" id="addTeamName" type="text" class="form-control" required />
|
||||
<label class="mt-1 form-label" for="addTeamLang">Language</label>
|
||||
<select id="addTeamLang" name="lang" class="form-select" required>
|
||||
<option value="Java">Java</option>
|
||||
<option value="CSharp">C#</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
on:click={() => {
|
||||
addModal.hide();
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-secondary">Cancel</button
|
||||
>
|
||||
<button type="submit" class="btn btn-success">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
<h1 style="text-align:center" class="mb-1"><i class="bi bi-people"></i> Teams</h1>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="text-end">
|
||||
{#if !adding}
|
||||
<button
|
||||
on:click={() => {
|
||||
adding = true;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-outline-success">Add</button
|
||||
>
|
||||
{/if}
|
||||
<button
|
||||
on:click={() => {
|
||||
addModal.show();
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-success">Add</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if adding}
|
||||
<form class="mb-3" method="POST" action="?/add" use:enhance>
|
||||
<h5>Name</h5>
|
||||
<input id="name" name="name" class="form-control" />
|
||||
<div class="mt-3 row">
|
||||
<div class="text-end">
|
||||
<button
|
||||
on:click={() => {
|
||||
adding = false;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-outline-secondary">Cancel</button
|
||||
>
|
||||
<button type="submit" class="btn btn-success">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Password</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -70,10 +153,44 @@
|
||||
<td>{team.id}</td>
|
||||
<td>{team.name}</td>
|
||||
<td
|
||||
><a href={`/admin/teams/${team.id.toString()}`} class="btn btn-sm btn-outline-secondary"
|
||||
>Details</a
|
||||
><span
|
||||
class="badge"
|
||||
class:bg-warning={team.language === 'Java'}
|
||||
class:bg-success={team.language === 'CSharp'}
|
||||
>
|
||||
{team.language}</span
|
||||
></td
|
||||
>
|
||||
<td><code>{team.password}</code></td>
|
||||
<td>
|
||||
<button
|
||||
on:click={() => {
|
||||
editTeam = team;
|
||||
editModal.show();
|
||||
}}
|
||||
class="btn btn-sm btn-outline-warning"><i class="bi bi-pencil-square"></i></button
|
||||
>
|
||||
<form
|
||||
action="?/delete"
|
||||
class="d-inline"
|
||||
method="POST"
|
||||
use:enhance={async ({ cancel }) => {
|
||||
if (
|
||||
!(await confirmModal.prompt(`Are you sure you want to delete team ${team.name}?`))
|
||||
) {
|
||||
cancel();
|
||||
}
|
||||
return async ({ update }) => {
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="teamId" value={team.id} />
|
||||
<button type="submit" class="btn btn-sm btn-danger"
|
||||
><i class="bi bi-trash3"></i></button
|
||||
>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
@ -1,46 +0,0 @@
|
||||
import { db } from '$lib/server/prisma';
|
||||
import { error } from 'console';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const load = (async ({ params }) => {
|
||||
const teamId = parseInt(params.teamId);
|
||||
if (isNaN(teamId)) {
|
||||
throw error(400, 'Invalid request');
|
||||
}
|
||||
const team = await db.team.findUnique({
|
||||
where: { id: teamId },
|
||||
select: { id: true, name: true, password: true }
|
||||
});
|
||||
if (!team) {
|
||||
throw redirect(302, '/admin/teams');
|
||||
}
|
||||
return { team: team };
|
||||
}) satisfies PageServerLoad;
|
||||
|
||||
export const actions = {
|
||||
password: async ({ request, params }) => {
|
||||
const data = await request.formData();
|
||||
const newPass = data.get('password');
|
||||
if (!newPass) {
|
||||
return { success: false };
|
||||
}
|
||||
try {
|
||||
await db.team.update({
|
||||
where: { id: parseInt(params.teamId) },
|
||||
data: { password: newPass.toString() }
|
||||
});
|
||||
} catch {
|
||||
return { success: false };
|
||||
}
|
||||
return { success: true };
|
||||
},
|
||||
delete: async ({ params }) => {
|
||||
try {
|
||||
await db.team.delete({ where: { id: parseInt(params.teamId) } });
|
||||
} catch {
|
||||
return { success: false };
|
||||
}
|
||||
throw redirect(302, '/admin/teams');
|
||||
}
|
||||
} satisfies Actions;
|
@ -1,103 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from '$app/forms';
|
||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||
import { genPassword } from '../util';
|
||||
import type { Actions, PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
export let form: Actions;
|
||||
|
||||
let changingPassword = false;
|
||||
|
||||
$: if (form && form.success) {
|
||||
changingPassword = false;
|
||||
}
|
||||
|
||||
function onGenPassword() {
|
||||
const passEntry = document.getElementById('pass_entry') as HTMLInputElement;
|
||||
passEntry.value = genPassword();
|
||||
}
|
||||
|
||||
let confirmModal: ConfirmModal;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Team - {data.team.name}</title>
|
||||
</svelte:head>
|
||||
|
||||
<ConfirmModal bind:this={confirmModal} />
|
||||
|
||||
<h1 style="text-align:center" class="mb-4"><i class="bi bi-people"></i> Team - {data.team.name}</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<a href="/admin/teams" class="mb-3 btn btn-outline-primary">All Teams</a>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<form
|
||||
method="POST"
|
||||
action="?/delete"
|
||||
use:enhance={async ({ cancel }) => {
|
||||
if ((await confirmModal.prompt('Are you sure?')) !== true) {
|
||||
cancel();
|
||||
}
|
||||
return async ({ update }) => {
|
||||
await update();
|
||||
};
|
||||
}}
|
||||
>
|
||||
<button type="submit" class="mb-3 btn btn-outline-danger">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Id</th>
|
||||
<th>Password</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{data.team.name}</td>
|
||||
<td>{data.team.id}</td>
|
||||
<td>{data.team.password}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{#if form && !form.success}
|
||||
<div class="alert alert-danger">Invalid entry</div>
|
||||
{/if}
|
||||
|
||||
{#if !changingPassword}
|
||||
<button
|
||||
on:click={() => {
|
||||
changingPassword = true;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-warning">Change Password</button
|
||||
>
|
||||
{:else}
|
||||
<form method="POST" action="?/password" use:enhance>
|
||||
<h4>Change Password</h4>
|
||||
<input id="pass_entry" name="password" class="form-control" />
|
||||
<div class="mt-2 row">
|
||||
<div class="text-end">
|
||||
<button
|
||||
on:click={() => {
|
||||
changingPassword = false;
|
||||
}}
|
||||
type="button"
|
||||
class="btn btn-outline-secondary">Cancel</button
|
||||
>
|
||||
<button on:click={onGenPassword} type="button" class="btn btn-outline-primary"
|
||||
>Generate</button
|
||||
>
|
||||
<button type="submit" class="btn btn-success">Change</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
Loading…
Reference in New Issue
Block a user