diff --git a/backend/api/database/room/crud.py b/backend/api/database/room/crud.py index 10f97af..76ec12f 100644 --- a/backend/api/database/room/crud.py +++ b/backend/api/database/room/crud.py @@ -5,7 +5,7 @@ from typing import List from fastapi import Depends, HTTPException, status, Query from pydantic import BaseModel from sqlalchemy import func -from sqlmodel import Session, delete, select, col +from sqlmodel import Session, delete, select, col, update from database.auth.crud import get_user_from_token from database.auth.models import User @@ -276,8 +276,8 @@ def refuse_waiter(member: Member, db: Session): def leave_room(member: Member, db: Session): - #db.execute(delete(Challenger).where(col(Challenger.member_id) == member.id)) - #db.execute(delete(TmpCorrection).where(col(TmpCorrection.member_id) == member.id)) + # db.execute(delete(Challenger).where(col(Challenger.member_id) == member.id)) + # db.execute(delete(TmpCorrection).where(col(TmpCorrection.member_id) == member.id)) db.delete(member) db.commit() return None @@ -333,6 +333,12 @@ def getChallenges(c: Challenger, db: Session): return challenges +def getMemberChallenges(m: Member, p: Parcours, db: Session): + challenges = db.exec(select(Challenge).where(Challenge.challenger_mid == m.id, + Challenge.challenger_pid == p.id)).all() + return challenges + + def getTops(p: Parcours, db: Session): tops = db.exec(select(Challenge).where(Challenge.parcours_id == p.id_code).order_by( col(Challenge.mistakes), col(Challenge.time)).limit(3)).all() @@ -379,6 +385,13 @@ def getMemberAvgRank(m: Member, p: Parcours, db: Session): return getAvgRank(challenger, p, db) +def getMemberValidated(m: Member, p: Parcours, db: Session): + challenger = db.exec(select(Challenger).where(Challenger.member_id == m.id)).first() + if challenger is None or challenger.validated is None: + return None + return challenger.validated + + def serialize_parcours(parcours: Parcours, member: Member, db: Session): tops = getTops(parcours, db) avgTop = getAvgTops(parcours, db) @@ -401,55 +414,13 @@ def serialize_parcours(parcours: Parcours, member: Member, db: Session): challengers = db.exec(statement).all() challs = {c.member.id_code: { - "challenger": {"id_code": c.member.id_code, "name": getUsername(c.member)}, + "challenger": {"id_code": c.member.id_code, "name": getUsername(c.member), "validated": c.validated}, # 'validated': chall.mistakes <= parcours.max_mistakes "challenges": [Challenges(**{**chall.dict(), "canCorrige": chall.data != []}) for chall in getChallenges(c, db)] } for c in challengers} return {**parcours.dict(), "pb": pb, "tops": tops, "challenges": challs, "rank": noteRank, "memberRank": avgRank, "validated": challenger.validated if challenger != None else False, "ranking": avgTop} - tops = [] - challs = {} - challenges = sorted(parcours.challenges, key=lambda x: ( - x.note['value'], x.time), reverse=True) - memberRank = None - rank = None - pb = None - validated = False - - total = 0 - - for i, chall in enumerate(challenges): - total += chall.note['value'] - id = chall.challenger.id_code - name = chall.challenger.user.username if chall.challenger.user_id != None else chall.challenger.anonymous.username - if i <= 2: - tops.append({"challenger": {"id_code": id, "name": name}, - "note": chall.note, "time": chall.time}) - - if id == member.id_code: - if challs.get(id) is None: - rank = i + 1 - memberRank = len(challs) + 1 - pb = {"note": chall.note, "time": chall.time} - if validated is False and chall.validated: - validated = True - - if member.is_admin or chall.challenger.id_code == member.id_code: - t = challs.get(id, {"total": 0})['total'] - challs[id] = {"challenger": {"id_code": id, "name": name - }, "challenges": [*challs.get(id, {'challenges': []})['challenges'], - Challenges( - **{**chall.dict(), "canCorrige": chall.data != []})], - "total": t + chall.note['value']} - - topMembers = [{**c['challenger'], "avg": c['total'] / - len(c['challenges'])} for id, c in challs.items()] - topMembers.sort(key=lambda x: x['avg'], reverse=True) - return {**parcours.dict(), "tops": tops, "challenges": challs, "rank": rank, "memberRank": memberRank, "pb": pb, - "validated": validated, - 'avg': None if len(parcours.challenges) == 0 else round(total / len(parcours.challenges), 2), - "ranking": topMembers} def change_anonymous_clientId(anonymous: Anonymous, db: Session): @@ -519,28 +490,23 @@ def deleteParcoursRelated(parcours: Parcours, db: Session): db.commit() -def change_challengers_validation(p: Parcours, validation: int, db: Session): - challengers = db.exec(select(Challenger).where( - Challenger.parcours_id == p.id)).all() - challs = [] - for c in challengers: - validated = c.best <= validation - if validated != c.validated: - c.validated = validated - challs.append(c) - - db.bulk_save_objects(challs) +def change_challengers_validation(p: Parcours, db: Session): + stmt = update(Challenger).values( + validated=select(Challenge.id).where(Challenge.challenger_mid == Challenger.member_id, + Challenge.challenger_pid == Challenger.parcours_id, + Challenge.validated == 1).exists()).where( + Challenger.parcours_id == p.id) + db.execute(stmt) db.commit() + return -def change_challenges_validation(p: Parcours, validation: int, db: Session): +def change_challenges_validation(p: Parcours, db: Session): challenges = db.exec(select(Challenge).where( Challenge.parcours_id == p.id_code)).all() - print('CHALLS', challenges) challs = [] for c in challenges: - validated = c.mistakes <= validation - print('CHAL', validated, c.validated, c) + validated = c.time <= p.time * 60 and c.mistakes <= p.max_mistakes if validated != c.validated: c.validated = validated challs.append(c) @@ -549,9 +515,10 @@ def change_challenges_validation(p: Parcours, validation: int, db: Session): db.commit() -def changeValidation(p: Parcours, validation: int, db: Session): - change_challengers_validation(p, validation, db) - change_challenges_validation(p, validation, db) +def changeValidation(p: Parcours, db: Session): + change_challenges_validation(p, db) + + change_challengers_validation(p, db) def compareExercices(old: list[Exercices], new: list[ExercicesCreate]): @@ -571,9 +538,7 @@ def update_parcours_db(parcours: ParcoursCreate, parcours_obj: Parcours, db: Ses update_challenges = True parcours_obj.exercices = exercices - if parcours_obj.max_mistakes != parcours.max_mistakes: - changeValidation(parcours_obj, parcours.max_mistakes, db) - + update_validated = parcours_obj.max_mistakes != parcours.max_mistakes or parcours_obj.time != parcours.time parcours_obj.name = parcours.name parcours_obj.time = parcours.time parcours_obj.max_mistakes = parcours.max_mistakes @@ -582,6 +547,8 @@ def update_parcours_db(parcours: ParcoursCreate, parcours_obj: Parcours, db: Ses db.commit() db.refresh(parcours_obj) + if update_validated: + changeValidation(parcours_obj, db) return parcours_obj, update_challenges @@ -736,7 +703,9 @@ def checkValidated(challenger: Challenger, db: Session, challenge: Challenge | N def create_challenge(data: List[CorrigedData], challenger: Member, parcours: Parcours, time: int, mistakes: int, isCorriged: bool, db: Session): challenger_obj: Challenger = getChallenger(parcours, challenger, db) - validated = mistakes <= parcours.max_mistakes + print('VALIDATING', time <= parcours.time * 60 and mistakes <= parcours.max_mistakes, time, parcours.time) + validated = time <= parcours.time * 60 and mistakes <= parcours.max_mistakes + challenge = Challenge(data=data, challenger_pid=challenger_obj.parcours_id, challenger_mid=challenger_obj.member_id, parcours=parcours, time=time, mistakes=mistakes, isCorriged=isCorriged, id_code=generate_unique_code(Challenge, s=db), validated=validated) @@ -775,7 +744,7 @@ def change_challenge(challenge: Challenge, corriged: CorrigedChallenge, db: Sess challenger.best = corriged['mistakes'] challenger.best_time = challenge.time - validated = corriged['mistakes'] <= parcours.max_mistakes + validated = challenge.time <= parcours.time * 60 and corriged['mistakes'] <= parcours.max_mistakes challenge.validated = validated if challenger.validated == False and validated: diff --git a/backend/api/database/room/models.py b/backend/api/database/room/models.py index 6cdda8c..e230595 100644 --- a/backend/api/database/room/models.py +++ b/backend/api/database/room/models.py @@ -145,6 +145,7 @@ class ParcoursReadUpdate(SQLModel): class ChallengerInfo(BaseModel): name: str id_code: str + validated: bool = False class ChallengerAverage(ChallengerInfo): diff --git a/backend/api/database7.db b/backend/api/database7.db index fdfd8f2..83abb7d 100644 Binary files a/backend/api/database7.db and b/backend/api/database7.db differ diff --git a/backend/api/routes/exercices/routes.py b/backend/api/routes/exercices/routes.py index dc2795a..fcc7145 100644 --- a/backend/api/routes/exercices/routes.py +++ b/backend/api/routes/exercices/routes.py @@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, Query, UploadFile, HTTPException, status from fastapi.responses import FileResponse, StreamingResponse from fastapi_pagination.ext.sqlalchemy_future import paginate as p from pydantic import BaseModel -from sqlmodel import Session, select +from sqlmodel import Session, select, col from database.auth.models import User from database.db import get_session @@ -85,6 +85,8 @@ def get_user_exercices(user: User = Depends(get_current_user), sub = select(ExercicesTagLink).where(ExercicesTagLink.exercice_id == Exercice.id).where( ExercicesTagLink.tag_id == t).exists() statement = statement.where(sub) + + statement = statement.order_by(col(Exercice.id).desc()) page = p(db, statement) exercices = page.items page.items = [ @@ -116,7 +118,7 @@ def get_public_exercices(user: User | None = Depends(get_current_user_optional), sub = select(ExercicesTagLink).where(ExercicesTagLink.exercice_id == Exercice.id).where( ExercicesTagLink.tag_id == t).exists() statement = statement.where(sub) - + statement = statement.order_by(col(Exercice.id).desc()) page = p(db, statement) print('¨PAGE', page) exercices = page.items diff --git a/backend/api/routes/room/manager.py b/backend/api/routes/room/manager.py index bd88b82..e511792 100644 --- a/backend/api/routes/room/manager.py +++ b/backend/api/routes/room/manager.py @@ -39,7 +39,6 @@ class RoomManager: if group in self.active_connections: for connection in list(set(self.active_connections[group])): - print(connection, connection.ws.state, connection.ws.client_state, connection.ws.application_state) if connection not in exclude and all(f(connection) for f in conditions): await self._send(connection, message, group) diff --git a/backend/api/routes/room/routes.py b/backend/api/routes/room/routes.py index 83a07a3..12b00ea 100644 --- a/backend/api/routes/room/routes.py +++ b/backend/api/routes/room/routes.py @@ -8,12 +8,12 @@ from sqlmodel import Session, select from database.auth.models import User from database.db import get_session from database.exercices.models import Exercice -from database.room.crud import delete_room_db +from database.room.crud import delete_room_db, getUsername, getMemberValidated from database.room.crud import serialize_parcours_short, change_correction, corrige_challenge, \ create_parcours_db, delete_parcours_db, create_room_db, get_member_dep, check_room, serialize_room, \ update_parcours_db, get_parcours, get_room, check_admin, get_exercices, get_challenge, get_correction, \ create_tmp_correction, create_challenge, change_challenge, serialize_parcours, getTops, getAvgRank, getRank, \ - getAvgTops, ChallengerFromChallenge, getMemberAvgRank, getMemberRank + getAvgTops, ChallengerFromChallenge, getMemberAvgRank, getMemberRank, getMemberChallenges from database.room.models import Challenge, ChallengeRead, Challenges, ParcoursReadUpdate, ChallengeInfo, Member, \ Parcours, ParcoursCreate, ParcoursRead, ParcoursReadShort, Room, RoomConnectionInfos, \ RoomCreate, RoomInfo, TmpCorrection, CorrigedData, CorrectionData @@ -45,7 +45,7 @@ def get_room_route(room: Room = Depends(get_room), member: Member = Depends(get_ return serialize_room(room, member, db) -@router.delete('/room/{room_id}', dependencies=[ Depends(check_admin) ]) +@router.delete('/room/{room_id}', dependencies=[Depends(check_admin)]) async def delete_room(room: Room = Depends(get_room), m: RoomManager = Depends(get_manager), db: Session = Depends(get_session)): delete_room_db(room, db) @@ -73,6 +73,8 @@ async def get_parcours_route(*, parcours: Parcours = Depends(get_parcours), memb return serialize_parcours(parcours, member, db) + + @router.put('/room/{room_id}/parcours/{parcours_id}', response_model=ParcoursRead) async def update_parcours(*, room_id: str, parcours: ParcoursCreate, member: Member = Depends(check_admin), parcours_old: Parcours = Depends(get_parcours), m: RoomManager = Depends(get_manager), @@ -88,9 +90,16 @@ async def update_parcours(*, room_id: str, parcours: ParcoursCreate, member: Mem await m.broadcast({"type": "edit_parcours", "data": { "parcours": ParcoursReadUpdate(**parcours_obj.dict(), update_challenges=update_challenges).dict()}}, parcours_old.id_code) + print('BROADCASTING') + await m.broadcast( + lambda m: {"type": "update_challenges", "data": {"challenger": {"id_code": m.id_code, "name": getUsername(m), "validated": getMemberValidated(m, parcours_obj, db)}, + "challenges": [Challenges( + **{**chall.dict(), "canCorrige": chall.data != []}).dict() for + chall in + getMemberChallenges(m, parcours_obj, db)]}}, + parcours_old.id_code, conditions=[lambda m: m.member.id_code != member.id_code]) return serialize_parcours(parcours_obj, member, db) - return {**parcours_obj.dict()} @router.delete('/room/{room_id}/parcours/{parcours_id}', dependencies=[Depends(check_admin)]) @@ -168,7 +177,7 @@ async def send_challenge(*, challenge: List[CorrectionData], correction: TmpCorr }}, parcours.id_code) print('CHALLENGE', chall) db.delete(correction) - returnValue = {**chall.dict(), 'validated': chall.mistakes <= correction.parcours.max_mistakes} + returnValue = {**chall.dict()} db.commit() return returnValue # return {**chall.dict(), 'validated': chall.mistakes <= correction.parcours.max_mistakes} @@ -243,7 +252,5 @@ async def corrige(*, correction: List[CorrigedData] = Body(), challenge: Challen @router.websocket('/ws/room/{room_id}') async def room_ws(ws: WebSocket, room: Room | None = Depends(check_room), db: Session = Depends(get_session), m: RoomManager = Depends(get_manager)): - - consumer = RoomConsumer(ws=ws, room=room, manager=m, db=db) await consumer.run() diff --git a/frontend/src/app.scss b/frontend/src/app.scss index 2ee2503..8f8b463 100644 --- a/frontend/src/app.scss +++ b/frontend/src/app.scss @@ -1,12 +1,13 @@ /* Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing */ * { - box-sizing: border-box; - margin: 0; + box-sizing: border-box; + margin: 0; } + .spinner { width: 30px; - height:30px; + height: 30px; border: 3px solid $contrast; border-bottom-color: transparent; border-radius: 50%; @@ -14,14 +15,15 @@ display: inline-block; box-sizing: border-box; - + } .italic { - font-style: italic; + font-style: italic; } + .underline { - text-decoration: underline; + text-decoration: underline; } @keyframes rotation { @@ -34,95 +36,109 @@ } .container { - height: calc(100vh - 100px); // 100% - nav + height: calc(100vh - 100px); // 100% - nav } -*{ - scrollbar-width: auto!important; +* { + scrollbar-width: auto !important; scrollbar-color: $contrast transparent; } .btn { - border: none; - border-radius: 5px; - height: 38px; - font-weight: 700; - transition: 0.3s; - margin-bottom: 10px; - margin-right: 7px; - padding: 0 50px; - width: max-content; - cursor: pointer; - &:disabled{ - cursor: not-allowed - } + border: none; + border-radius: 5px; + height: 38px; + font-weight: 700; + transition: 0.3s; + margin-bottom: 10px; + margin-right: 7px; + padding: 0 50px; + width: max-content; + cursor: pointer; + + &:disabled { + cursor: not-allowed + } } .primary-btn { - @extend .btn; - background-color: #fcbf49; - &:hover { - background-color: #ac7b19; - } + @extend .btn; + background-color: #fcbf49; + + &:hover { + background-color: #ac7b19; + } } .danger-btn { - @extend .btn; - background-color: #fc5e49; - &:hover { - background-color: #ac1919; - } + @extend .btn; + background-color: #fc5e49; + + &:hover { + background-color: #ac1919; + } } .border-primary-btn { - @extend .btn; - background-color: transparent; - border: 1px solid #fcbf49; - color: #fcbf49; - &:hover { - background-color: #fcbf49; - color: black; - } + @extend .btn; + background-color: transparent; + border: 1px solid #fcbf49; + color: #fcbf49; + + &:hover { + background-color: #fcbf49; + color: black; + } } .input { - background-color: inherit; - color: inherit; - padding: 5px 10px; - width: 100%; - font-size: 16px; - font-weight: 450; - margin: 10px 0; - float: left; - border: none; - border-bottom: 1px solid #181553; - transition: 0.3s; - border-radius: 0; - margin: 0; - &:focus { - outline: none; - border-bottom-color: $contrast; - } + background-color: inherit; + color: inherit; + padding: 5px 10px; + width: 100%; + font-size: 16px; + font-weight: 450; + margin: 10px 0; + float: left; + border: none; + border-bottom: 1px solid #181553; + transition: 0.3s; + border-radius: 0; + margin: 0; + + &:focus { + outline: none; + border-bottom-color: $contrast; + } } .flex-row-center { - display: flex; - justify-content: center; + display: flex; + justify-content: center; } + @for $f from 0 through 100 { - .wp-#{$f} { - width: 1% * $f; - } + .wp-#{$f} { + width: 1% * $f; + } } -.sv-dropdown{ - z-index: 10!important; +.sv-dropdown { + z-index: 10 !important; } -.strong{ - font-weight: 900; +.strong { + font-weight: 900; } +.contrast { + color: $contrast; +} + + +.loading { + cursor: progress; +} \ No newline at end of file diff --git a/frontend/src/components/NavBar.svelte b/frontend/src/components/NavBar.svelte index 8db8650..f39cf6b 100644 --- a/frontend/src/components/NavBar.svelte +++ b/frontend/src/components/NavBar.svelte @@ -5,7 +5,7 @@ import FaHome from "svelte-icons/fa/FaHome.svelte"; import { afterNavigate } from "$app/navigation"; import FaUser from "svelte-icons/fa/FaUser.svelte"; - import FaSignOutAlt from "svelte-icons/fa/FaSignOutAlt.svelte"; + import IoIosLogOut from 'svelte-icons/io/IoIosLogOut.svelte' const { isAuth, @@ -21,40 +21,41 @@ \ No newline at end of file diff --git a/frontend/src/components/auth/InfoForm.svelte b/frontend/src/components/auth/InfoForm.svelte index e59b642..e631e34 100644 --- a/frontend/src/components/auth/InfoForm.svelte +++ b/frontend/src/components/auth/InfoForm.svelte @@ -1,46 +1,51 @@ {#if !!$myForm} -
- - - - -
+
+ + + + +
{/if} diff --git a/frontend/src/components/auth/PasswordForm.svelte b/frontend/src/components/auth/PasswordForm.svelte index 64f7cc8..a23890d 100644 --- a/frontend/src/components/auth/PasswordForm.svelte +++ b/frontend/src/components/auth/PasswordForm.svelte @@ -1,31 +1,35 @@ {#if !!$myForm} -
- - -
+
+ + +
{/if} \ No newline at end of file diff --git a/frontend/src/components/auth/Section.svelte b/frontend/src/components/auth/Section.svelte index cd4ea28..4b3c3eb 100644 --- a/frontend/src/components/auth/Section.svelte +++ b/frontend/src/components/auth/Section.svelte @@ -1,34 +1,41 @@
-

-
-
- {title}

-
- +

+
+ +
+ {title}

+
+ +
+ {#if !!onValidate} +
+
- {#if !!onValidate} -
- -
- {/if} + {/if}
\ No newline at end of file diff --git a/frontend/src/components/exos/Card.svelte b/frontend/src/components/exos/Card.svelte index 2c507eb..b3021e0 100644 --- a/frontend/src/components/exos/Card.svelte +++ b/frontend/src/components/exos/Card.svelte @@ -1,71 +1,71 @@
{}} on:keypress={() => {}}> -

{exo.name}

-
- {#if exo.examples != null} -

Exemples

- {#if !!exo.consigne}

{exo.consigne}

{/if} - {#each exo.examples.data.slice(0, 3) as ex} -

{ex.calcul}

- {/each} - {:else} -

Aucun exemple disponible

- {/if} -
- {#if !!$isAuth && exo.is_author && exo.original == null } -
- - {exo.private == true ? 'Privé' : 'Public'} -
- {:else if !exo.is_author} -
- - Par {exo.author.username} - - {#if !!$isAuth} -
{}} - on:click|stopPropagation={() => { +

{exo.name}

+
+ {#if exo.examples != null} +

Exemples

+ {#if !!exo.consigne}

{exo.consigne}

{/if} + {#each exo.examples.data.slice(0, 3) as ex} +

{ex.calcul}

+ {/each} + {:else} +

Aucun exemple disponible

+ {/if} +
+ {#if !!$isAuth && exo.is_author && exo.original == null } +
+ + {exo.private == true ? 'Privé' : 'Public'} +
+ {:else if !exo.is_author} +
+ + Par {exo.author.username} + + {#if !!$isAuth} +
{}} + on:click|stopPropagation={() => { cloneExo(exo.id_code).then((r) => { goto('/exercices/' + r.id_code); show(ModalCard, { exo: r }, () => { @@ -73,121 +73,133 @@ }); }); }} - > - -
- {/if} -
- {:else if exo.is_author && exo.original != null} -
- Par {exo.original?.author} -
- {/if} -
- {#if !!$isAuth} - - {/if} - + > + +
+ {/if} +
+ {:else if exo.is_author && exo.original != null} +
+ Par {exo.original?.author} +
+ {/if} +
+ {#if !!$isAuth} + + {/if} +
diff --git a/frontend/src/components/exos/CreateCard.svelte b/frontend/src/components/exos/CreateCard.svelte index 3ae680f..e129406 100644 --- a/frontend/src/components/exos/CreateCard.svelte +++ b/frontend/src/components/exos/CreateCard.svelte @@ -1,33 +1,43 @@
-

Nouvel exercice

- +

Nouvel exercice

+
diff --git a/frontend/src/components/exos/DownloadForm.svelte b/frontend/src/components/exos/DownloadForm.svelte index bff9aed..050f48d 100644 --- a/frontend/src/components/exos/DownloadForm.svelte +++ b/frontend/src/components/exos/DownloadForm.svelte @@ -86,7 +86,7 @@ {/if}
-
close()} on:keypress={() => {}}> +
close()} on:keypress={() => {}}>
@@ -148,6 +148,7 @@ {/if} diff --git a/frontend/src/components/exos/ExoList.svelte b/frontend/src/components/exos/ExoList.svelte index d664cd4..a7ffb58 100644 --- a/frontend/src/components/exos/ExoList.svelte +++ b/frontend/src/components/exos/ExoList.svelte @@ -137,6 +137,7 @@
diff --git a/frontend/src/components/exos/Head.svelte b/frontend/src/components/exos/Head.svelte index fd5a545..58f0478 100644 --- a/frontend/src/components/exos/Head.svelte +++ b/frontend/src/components/exos/Head.svelte @@ -70,6 +70,7 @@
diff --git a/frontend/src/components/exos/Pagination.svelte b/frontend/src/components/exos/Pagination.svelte index 024825d..2211898 100644 --- a/frontend/src/components/exos/Pagination.svelte +++ b/frontend/src/components/exos/Pagination.svelte @@ -104,7 +104,6 @@ display: flex; margin: 30px; height: max-content; - width: 100%; justify-content: center; align-items: center; button { diff --git a/frontend/src/components/rooms/Challenge.svelte b/frontend/src/components/rooms/Challenge.svelte index 3292e9f..5b9475d 100644 --- a/frontend/src/components/rooms/Challenge.svelte +++ b/frontend/src/components/rooms/Challenge.svelte @@ -1,91 +1,91 @@ -{#if $challengeStore != null} -
-

- {$challengeStore.parcours.name} +
+ {#if $challengeStore != null} +
+

+ {$challengeStore.parcours.name} - {#if corrige && !!$challengeStore.challenger && remaining != null} + {#if corrige && !!$challengeStore.challenger && remaining != null} - - Correction de {$challengeStore.parcours.name} par + Correction de {$challengeStore.parcours.name} par {$challengeStore.challenger.name} en {parseTimer(remaining)} (cliquez sur les réponses pour voir) - {:else} + > + {:else} { + class="icon" + on:click={() => { challenge($room.id_code, id_code, !$member.isUser ? $member.clientId : null).then( (p) => { challengeStore.set({ ...p, corriged: false }); @@ -97,63 +97,67 @@ } ); }} - title={'Réessayer'} - on:keydown={() => {}}> - {/if} -

- {#if $challengeStore.mistakes} - {$challengeStore.mistakes} fautes - {/if} - {#if !corrige} -

- {remaining != null && parseTimer(remaining)} -

- {/if} -
+ title={'Réessayer'} + on:keydown={() => {}}> + {/if} +

- {#each $challengeStore.challenge as e, d (`${$challengeStore.id_code}_${d}`)} -
-
-

Exercice {d + 1} : {e.exo.name}

-

- {e.exo.consigne} -

-
-
- {#each e.data as c, a} -
- {#each c.calcul.replace(']', '] ').replace('[', ' [').split(' ') as i, b} - {#if i.startsWith('[') && i.endsWith(']')} - {$challengeStore.mistakes} fautes

+ {/if} + + {#if !corrige} +

+ {remaining != null && parseTimer(remaining)} +

+ {/if} +
+ + {#each $challengeStore.challenge as e, d (`${$challengeStore.id_code}_${d}`)} +
+
+

Exercice {d + 1} : {e.exo.name}

+ {#if e.exo.consigne != null} +

+ {e.exo.consigne} +

+ {/if} +
+
+ {#each e.data as c, a} +
+ {#each c.calcul.replace(']', '] ').replace('[', ' [').split(' ') as i, b} + {#if i.startsWith('[') && i.endsWith(']')} + - {:else} - {i}{' '} - {/if} - {/each} -
- {/each} -
-
- {/each} -
- {#if !corrige} -
+ {/each} +
+
+ {/each} +
+ {#if !corrige} + - + - {:else if $member.isAdmin} - + {:else if $member.isAdmin} + - {/if} + }}>Valider ! + + {/if} - -
-{/if} - + }}>{!$challengeStore.corriged ? "Annuler !" : "Retour"} +
+ {/if} +
diff --git a/frontend/src/components/rooms/ChallengesList.svelte b/frontend/src/components/rooms/ChallengesList.svelte index 16378d3..f8087e9 100644 --- a/frontend/src/components/rooms/ChallengesList.svelte +++ b/frontend/src/components/rooms/ChallengesList.svelte @@ -1,113 +1,134 @@ {#if $parcours != null && $member != null} -
- {#if Object.keys($parcours.challenges).length == 0} -

Aucun essai effectué :(

- {/if} +
+ {#if Object.keys($parcours.challenges).length == 0} +

Aucun essai effectué :(

+ {/if} - {#each Object.entries($parcours.challenges) as [id, chall]} -

{ + {#each Object.entries($parcours.challenges) as [id, chall]} +

{ selected = selected == chall.challenger.id_code ? '' : chall.challenger.id_code; }} - class:selected={selected == chall.challenger.id_code} - class="tries" - on:keydown={() => {}} - > - - {chall.challenger.id_code == $member.id_code ? 'Vos essais' : chall.challenger.name} -

+ class:selected={selected == chall.challenger.id_code} + class="tries" + on:keydown={() => {}} + > + + {chall.challenger.id_code == $member.id_code ? 'Vos essais' : chall.challenger.name} + {chall.challenger.validated ? "Validé": "Non validé"} +

- {#if selected == chall.challenger.id_code} - {#each chall.challenges as c} -
{ + {#if selected == chall.challenger.id_code} + {#each chall.challenges as c} +
{ goto(`?${new URLSearchParams({corr: c.id_code}).toString()}`); }} - on:keydown={() => {}} - title="Voir la correction" - > -

{c.mistakes} faute{c.mistakes > 1 ?"s": ""} en {parseTimer(c.time)} -

- -
- {/each} - {/if} - {/each} -
+ on:keydown={() => {}} + title="Voir la correction" + > +

{c.mistakes} faute{c.mistakes > 1 ? "s" : ""} en {c.time < $parcours.time * 60 ? parseTimer(c.time): parseTimer($parcours.time*60)} {c.time > $parcours.time * 60? `( + ${parseTimer(c.time - $parcours.time*60)} )`:""} +

+ +
+ {/each} + {/if} + {/each} +
{/if} diff --git a/frontend/src/components/rooms/RoomHead.svelte b/frontend/src/components/rooms/RoomHead.svelte index fdd702e..09237b5 100644 --- a/frontend/src/components/rooms/RoomHead.svelte +++ b/frontend/src/components/rooms/RoomHead.svelte @@ -4,8 +4,7 @@ import FaUndo from "svelte-icons/fa/FaUndo.svelte"; import FaTimes from "svelte-icons/fa/FaTimes.svelte"; - import FaSignOutAlt from "svelte-icons/fa/FaSignOutAlt.svelte"; - + import IoMdLogOut from 'svelte-icons/io/IoMdLogOut.svelte' import { goto } from "$app/navigation"; import type { Writable } from "svelte/store"; import type { Member, Room } from "../../types/room.type"; @@ -84,7 +83,7 @@ }} on:keypress={()=>{}} > - +
{/if} diff --git a/frontend/src/context/Auth.svelte b/frontend/src/context/Auth.svelte index 10945af..6fab86a 100644 --- a/frontend/src/context/Auth.svelte +++ b/frontend/src/context/Auth.svelte @@ -94,7 +94,7 @@ if (checkExpire(exp) && refresh != null) { refreshRequest(refresh).then((r) => { localStorage.setItem('token', r.access_token); - $username = username; + $username = name; $isAuth = true; }); diff --git a/frontend/src/context/Modal.svelte b/frontend/src/context/Modal.svelte index 5312f96..b3e6086 100644 --- a/frontend/src/context/Modal.svelte +++ b/frontend/src/context/Modal.svelte @@ -69,16 +69,19 @@ position: fixed; top: 50%; left: 50%; - width: 50%; + //width: 50%; transform: translateX(-50%) translateY(-50%) scale(0.7); visibility: hidden; transition: 0.4s; opacity: 0; z-index: 1000; - height: 57vh; + max-height: 57vh; display: flex; flex-direction: column; justify-content: center; + align-items: center; + + &.visible { visibility: visible !important; transform: translateX(-50%) translateY(-50%) scale(1) !important; @@ -95,7 +98,7 @@ .overlay { background-color: black; opacity: 0; - z-index: 999; + z-index: 500; width: 100%; position: absolute; top: 0; diff --git a/frontend/src/context/Notification.svelte b/frontend/src/context/Notification.svelte index 415aff8..cffd920 100644 --- a/frontend/src/context/Notification.svelte +++ b/frontend/src/context/Notification.svelte @@ -1,160 +1,174 @@ -
- {#each $notifications as n} -
{ +
+ {#each $notifications as n} +
{ n.deleted = true; setTimeout(() => { notifications.update((o) => o.filter((i) => i.id != n.id)); }, 500); }} - class={n.type} - on:keydown={() => {}} - class:deleted={n.deleted} - > -

{n.title}

-

-
- {/each} + class={n.type} + on:keydown={() => {}} + class:deleted={n.deleted} + > +

{n.title}

+

+ +

+
+ {/each}
diff --git a/frontend/src/requests/auth.request.ts b/frontend/src/requests/auth.request.ts index d086672..68acbce 100644 --- a/frontend/src/requests/auth.request.ts +++ b/frontend/src/requests/auth.request.ts @@ -1,5 +1,6 @@ import axios from 'axios'; import {authInstance} from '../apis/auth.api'; +import { env } from "$env/dynamic/public"; export const loginRequest = (data: { username: string; password: string }) => { return authInstance @@ -37,9 +38,9 @@ export const registerRequest = (data: { }; export const refreshRequest = (token: string) => { - return authInstance + return axios .request({ - url: '/refresh', + url: `${env.PUBLIC_API_BASE}/refresh`, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', diff --git a/frontend/src/requests/exo.request.ts b/frontend/src/requests/exo.request.ts index 5ff0978..c3c1aa9 100644 --- a/frontend/src/requests/exo.request.ts +++ b/frontend/src/requests/exo.request.ts @@ -131,9 +131,11 @@ export const getExoSource = (id_code: string,) => { link.click(); link.remove(); }); -};export const generateRequest = (id_code: string,filename: string) => { +}; + +export const generateRequest = (id_code: string,filename: string) => { return exoInstance({ - url: `/generator/csv/${id_code}/`, + url: `generator/csv/${id_code}`, method: 'Get', params: {filename} }).then((r) => { diff --git a/frontend/src/requests/room.request.ts b/frontend/src/requests/room.request.ts index 816ee0d..93af0b7 100644 --- a/frontend/src/requests/room.request.ts +++ b/frontend/src/requests/room.request.ts @@ -4,7 +4,7 @@ import { roomInstance } from '../apis/room.api'; export const createRoom = (data: { name: string }, username: string | null = null) => { return roomInstance .request({ - url: '/', + url: '', method: 'POST', params: { username }, data: { ...data, public: false, global_results: false } diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index c23829d..a23f0ad 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -79,7 +79,7 @@ height: calc(100vh - var(--navbar-height) - 10px); overflow: auto; height: 100%; - + overflow-x: hidden; a { color: red; } diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 47275ae..d20495e 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,10 +1,30 @@ - - +
+

Générateur d'exercices

+
+ + +
+
+ diff --git a/frontend/src/routes/dashboard/+page.svelte b/frontend/src/routes/dashboard/+page.svelte index 71f7a39..c9785d0 100644 --- a/frontend/src/routes/dashboard/+page.svelte +++ b/frontend/src/routes/dashboard/+page.svelte @@ -1,42 +1,42 @@ -
- {#if $user != null} -

Mon compte

-
{ + {#if $user != null} +

Mon compte

+
{ if(infoForm == null) return updateUserRequest($infoForm.summary).then(res=>{ user.update((o)=>{return {...o,...res}}) @@ -46,12 +46,12 @@ }) }} canValid={infoForm != null && $infoForm.valid}> - -
-
- -
-
{ + +
+
+ +
+
{ show(UserConfirm, {onValidate: ( p )=>{ updatePassword({...$passwordForm.summary, old_password: p}).then((r)=>{ localStorage.setItem("token", r.access_token) @@ -65,15 +65,24 @@ }, validate: "Changer mon mot de passe", cancel: close, cancelMsg: "Garder le mot de passe actuel"}) }} canValid={passwordForm != null && $passwordForm.valid}> - -
- {/if} + +
+ {/if}
- \ No newline at end of file diff --git a/frontend/src/routes/room/+page.svelte b/frontend/src/routes/room/+page.svelte index d2cf850..a8e81bb 100644 --- a/frontend/src/routes/room/+page.svelte +++ b/frontend/src/routes/room/+page.svelte @@ -3,14 +3,27 @@ import {goto} from "$app/navigation"; -
+

Salles

-
+
\ No newline at end of file diff --git a/frontend/src/routes/room/[...slug]/+page.svelte b/frontend/src/routes/room/[...slug]/+page.svelte index caff96e..8899508 100644 --- a/frontend/src/routes/room/[...slug]/+page.svelte +++ b/frontend/src/routes/room/[...slug]/+page.svelte @@ -63,7 +63,6 @@ if ($page.url.searchParams.get("a") == "waiting") { goto(`?`); } - console.log("ACCEPTED", data.member); member.set(data.member); getRoom($page.params.slug, !$isAuth ? data.member.clientId : null).then((r) => { @@ -83,7 +82,7 @@ case "waiting": - close() + close(); $member = { ...data.waiter, room: data.room }; goto(`?${new URLSearchParams({ a: "waiting" })}`); return; @@ -117,9 +116,9 @@ case "deleted": info("Suppression", "La salle a été supprimée par l'administrateur"); - ws.close(1000) - goto("/room/join") - return + ws.close(1000); + goto("/room/join"); + return; case "error": const { code, msg } = data; @@ -157,9 +156,9 @@ } else { error("Erreur", "Message : " + msg); } - if(code == 404){ - ws.close(1000) - goto("/room/join") + if (code == 404) { + ws.close(1000); + goto("/room/join"); } return; case "waiter": @@ -360,7 +359,7 @@ } } case "challenge_change": - if ($parcours != null && !!$parcours.challenges[data.member]) { + if ($parcours != null && $member != null && !!$parcours.challenges[data.member]) { $parcours.challenges[data.member].challenges = $parcours.challenges[ data.member ].challenges.map((c) => { @@ -369,8 +368,24 @@ } return c; }); - $parcours.validated = data.validated; + $parcours.challenges[data.member].challenger.validated = data.validated; + if (data.member == $member.id_code) { + $parcours.validated = data.validated; + } } + return; + + case "update_challenges": + if ($parcours != null && $member != null) { + const { challenger, challenges } = data; + if (challenges.length != 0) { + $parcours.challenges[challenger.id_code] = data; + } + if (challenger.id_code == $member.id_code) { + $parcours.validated = data.challenger.validated; + } + } + return; } }; diff --git a/frontend/src/routes/room/create/+page.svelte b/frontend/src/routes/room/create/+page.svelte index bab7312..b103dd0 100644 --- a/frontend/src/routes/room/create/+page.svelte +++ b/frontend/src/routes/room/create/+page.svelte @@ -24,7 +24,7 @@ if(!$isAuth){ sessionStorage.setItem('reconnect', r.member) } - goto(`/room/${r.room}`); + goto(`/room/${r.room}`); }); }} >