Salles
This commit is contained in:
parent
90b48e710f
commit
9393c88f8e
@ -59,9 +59,15 @@ def change_room_status(room: Room, public: bool, db: Session):
|
|||||||
return room
|
return room
|
||||||
|
|
||||||
|
|
||||||
|
def delete_room_db(room: Room, db: Session):
|
||||||
|
db.delete(room)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_member_from_user(user_id: int, room_id: int, db: Session):
|
def get_member_from_user(user_id: int, room_id: int, db: Session):
|
||||||
member = db.exec(select(Member).where(Member.room_id ==
|
member = db.exec(select(Member).where(Member.room_id ==
|
||||||
room_id, Member.user_id == user_id)).first()
|
room_id, Member.user_id == user_id)).first()
|
||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +83,7 @@ def get_member_from_token(token: str, room_id: int, db: Session):
|
|||||||
|
|
||||||
def get_member_from_anonymous(anonymous_id: int, room_id: int, db: Session):
|
def get_member_from_anonymous(anonymous_id: int, room_id: int, db: Session):
|
||||||
member = db.exec(select(Member).where(Member.room_id ==
|
member = db.exec(select(Member).where(Member.room_id ==
|
||||||
room_id, Member.anonymous_id == anonymous_id)).first()
|
room_id, Member.anonymous_id == anonymous_id)).first()
|
||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
@ -109,7 +115,8 @@ def get_member_from_clientId(clientId: str, room_id: int, db: Session):
|
|||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
def create_member(*, room: Room, user: User | None = None, anonymous: Anonymous | None = None, waiting: bool = False, db: Session):
|
def create_member(*, room: Room, user: User | None = None, anonymous: Anonymous | None = None, waiting: bool = False,
|
||||||
|
db: Session):
|
||||||
member_id = generate_unique_code(Member, s=db)
|
member_id = generate_unique_code(Member, s=db)
|
||||||
member = Member(room=room, user=user, anonymous=anonymous, waiting=waiting,
|
member = Member(room=room, user=user, anonymous=anonymous, waiting=waiting,
|
||||||
id_code=member_id)
|
id_code=member_id)
|
||||||
@ -154,7 +161,7 @@ def disconnect_member(member: Member, db: Session):
|
|||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
def validate_username(username: str, room: Room, db: Session = Depends(get_session)):
|
def validate_username(username: str, room: Room, db: Session = Depends(get_session)):
|
||||||
if len(username) > 20:
|
if len(username) > 20:
|
||||||
return None
|
return None
|
||||||
members = select(Member.anonymous_id).where(
|
members = select(Member.anonymous_id).where(
|
||||||
@ -269,6 +276,8 @@ def refuse_waiter(member: Member, db: Session):
|
|||||||
|
|
||||||
|
|
||||||
def leave_room(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.delete(member)
|
db.delete(member)
|
||||||
db.commit()
|
db.commit()
|
||||||
return None
|
return None
|
||||||
@ -525,7 +534,6 @@ def change_challengers_validation(p: Parcours, validation: int, db: Session):
|
|||||||
|
|
||||||
|
|
||||||
def change_challenges_validation(p: Parcours, validation: int, db: Session):
|
def change_challenges_validation(p: Parcours, validation: int, db: Session):
|
||||||
|
|
||||||
challenges = db.exec(select(Challenge).where(
|
challenges = db.exec(select(Challenge).where(
|
||||||
Challenge.parcours_id == p.id_code)).all()
|
Challenge.parcours_id == p.id_code)).all()
|
||||||
print('CHALLS', challenges)
|
print('CHALLS', challenges)
|
||||||
@ -833,7 +841,7 @@ def check_admin(member: Member = Depends(get_member_dep)):
|
|||||||
|
|
||||||
def get_parcours(parcours_id: str, room: Room = Depends(get_room), db: Session = Depends(get_session)):
|
def get_parcours(parcours_id: str, room: Room = Depends(get_room), db: Session = Depends(get_session)):
|
||||||
room = db.exec(select(Parcours).where(Parcours.id_code ==
|
room = db.exec(select(Parcours).where(Parcours.id_code ==
|
||||||
parcours_id, Parcours.room_id == room.id)).first()
|
parcours_id, Parcours.room_id == room.id)).first()
|
||||||
if room is None:
|
if room is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail="Parcours introuvable")
|
status_code=status.HTTP_404_NOT_FOUND, detail="Parcours introuvable")
|
||||||
@ -844,10 +852,12 @@ def get_exercices(parcours: Parcours = Depends(get_parcours), db: Session = Depe
|
|||||||
exercices = db.exec(select(Exercice).where(col(Exercice.id_code).in_(
|
exercices = db.exec(select(Exercice).where(col(Exercice.id_code).in_(
|
||||||
[e['exercice_id'] for e in parcours.exercices]))).all()
|
[e['exercice_id'] for e in parcours.exercices]))).all()
|
||||||
|
|
||||||
return [{"exercice": e, "quantity": [q for q in parcours.exercices if q['exercice_id'] == e.id_code][0]['quantity']} for e in exercices]
|
return [{"exercice": e, "quantity": [q for q in parcours.exercices if q['exercice_id'] == e.id_code][0]['quantity']}
|
||||||
|
for e in exercices]
|
||||||
|
|
||||||
|
|
||||||
def get_correction(correction_id: str, parcours_id: str, member: Member = Depends(get_member_dep), db: Session = Depends(get_session)):
|
def get_correction(correction_id: str, parcours_id: str, member: Member = Depends(get_member_dep),
|
||||||
|
db: Session = Depends(get_session)):
|
||||||
tmpCorr = db.exec(select(TmpCorrection).where(
|
tmpCorr = db.exec(select(TmpCorrection).where(
|
||||||
TmpCorrection.id_code == correction_id, TmpCorrection.parcours_id == parcours_id)).first()
|
TmpCorrection.id_code == correction_id, TmpCorrection.parcours_id == parcours_id)).first()
|
||||||
if tmpCorr is None:
|
if tmpCorr is None:
|
||||||
|
@ -24,8 +24,8 @@ class Room(RoomBase, table=True):
|
|||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
id_code: str = Field(index=True)
|
id_code: str = Field(index=True)
|
||||||
|
|
||||||
members: List['Member'] = Relationship(back_populates="room")
|
members: List['Member'] = Relationship(back_populates="room",sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
parcours: List['Parcours'] = Relationship(back_populates="room")
|
parcours: List['Parcours'] = Relationship(back_populates="room",sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
|
|
||||||
|
|
||||||
class AnonymousBase(SQLModel):
|
class AnonymousBase(SQLModel):
|
||||||
@ -58,7 +58,9 @@ class Member(SQLModel, table=True):
|
|||||||
room_id: int = Field(foreign_key="room.id")
|
room_id: int = Field(foreign_key="room.id")
|
||||||
room: Room = Relationship(back_populates='members')
|
room: Room = Relationship(back_populates='members')
|
||||||
|
|
||||||
challengers: List["Challenger"] = Relationship(back_populates="member")
|
challengers: List["Challenger"] = Relationship(back_populates="member", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
|
corrections: List["TmpCorrection"] = Relationship(back_populates="member", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
|
|
||||||
|
|
||||||
is_admin: bool = False
|
is_admin: bool = False
|
||||||
|
|
||||||
@ -68,7 +70,6 @@ class Member(SQLModel, table=True):
|
|||||||
|
|
||||||
waiter_code: Optional[str] = Field(default=None)
|
waiter_code: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
corrections: List['TmpCorrection'] = Relationship(back_populates="member")
|
|
||||||
|
|
||||||
|
|
||||||
class ExercicesCreate(SQLModel):
|
class ExercicesCreate(SQLModel):
|
||||||
@ -101,7 +102,7 @@ class Parcours(SQLModel, table=True):
|
|||||||
room_id: int = Field(foreign_key="room.id")
|
room_id: int = Field(foreign_key="room.id")
|
||||||
room: Room = Relationship(back_populates='parcours')
|
room: Room = Relationship(back_populates='parcours')
|
||||||
|
|
||||||
challengers: list[Challenger] = Relationship(back_populates="parcours")
|
challengers: list[Challenger] = Relationship(back_populates="parcours", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
time: int
|
time: int
|
||||||
@ -110,9 +111,9 @@ class Parcours(SQLModel, table=True):
|
|||||||
max_mistakes: int
|
max_mistakes: int
|
||||||
|
|
||||||
exercices: List[Exercices] = Field(sa_column=Column(JSON))
|
exercices: List[Exercices] = Field(sa_column=Column(JSON))
|
||||||
challenges: List["Challenge"] = Relationship(back_populates="parcours")
|
challenges: List["Challenge"] = Relationship(back_populates="parcours", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
corrections: List["TmpCorrection"] = Relationship(
|
corrections: List["TmpCorrection"] = Relationship(
|
||||||
back_populates="parcours")
|
back_populates="parcours", sa_relationship_kwargs={"cascade": "all, delete, delete-orphan"})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ from database.auth.crud import get_user_from_token
|
|||||||
from database.room.crud import change_room_name, change_room_status, serialize_member, check_user_in_room, \
|
from database.room.crud import change_room_name, change_room_status, serialize_member, check_user_in_room, \
|
||||||
create_anonymous, create_member, get_member, get_member_from_token, get_member_from_reconnect_code, connect_member, \
|
create_anonymous, create_member, get_member, get_member_from_token, get_member_from_reconnect_code, connect_member, \
|
||||||
disconnect_member, get_waiter, accept_waiter, leave_room, refuse_waiter
|
disconnect_member, get_waiter, accept_waiter, leave_room, refuse_waiter
|
||||||
from database.room.models import Room, Member, MemberRead, Waiter
|
from database.room.models import Room, Member, MemberRead, Waiter, Challenger
|
||||||
from services.websocket import Consumer
|
from services.websocket import Consumer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class RoomConsumer(Consumer):
|
class RoomConsumer(Consumer):
|
||||||
|
|
||||||
def __init__(self, ws: WebSocket, room: Room, manager: "RoomManager", db: Session):
|
def __init__(self, ws: WebSocket, room: Room | None, manager: "RoomManager", db: Session):
|
||||||
self.room = room
|
self.room = room
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
@ -25,14 +25,22 @@ class RoomConsumer(Consumer):
|
|||||||
self.member = None
|
self.member = None
|
||||||
self.banned = False
|
self.banned = False
|
||||||
|
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
await self.ws.accept()
|
||||||
|
if self.room is None:
|
||||||
|
await self.send_error("Salle introuvable", code=404)
|
||||||
|
await self.ws.close()
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
# WS Utilities
|
# WS Utilities
|
||||||
async def send(self, payload: Any | Callable):
|
async def send(self, payload: Any | Callable):
|
||||||
if callable(payload):
|
if callable(payload):
|
||||||
payload = payload(self.member)
|
payload = payload(self.member)
|
||||||
return await super().send(payload)
|
return await super().send(payload)
|
||||||
|
|
||||||
async def connect(self):
|
|
||||||
await self.ws.accept()
|
|
||||||
|
|
||||||
async def direct_send(self, type: str, payload: Any, code: int | None = None):
|
async def direct_send(self, type: str, payload: Any, code: int | None = None):
|
||||||
sending = {'type': type, "data": payload, }
|
sending = {'type': type, "data": payload, }
|
||||||
@ -243,12 +251,13 @@ class RoomConsumer(Consumer):
|
|||||||
|
|
||||||
@Consumer.event('leave', conditions=[isMember])
|
@Consumer.event('leave', conditions=[isMember])
|
||||||
async def leave(self):
|
async def leave(self):
|
||||||
|
print('LEAVED', self.member, isinstance(self.member, Member), isinstance(self.member, Challenger))
|
||||||
if self.member.is_admin is True:
|
if self.member.is_admin is True:
|
||||||
await self.send_error("Vous ne pouvez pas quitter une salle dont vous êtes l'administrateur")
|
await self.send_error("Vous ne pouvez pas quitter une salle dont vous êtes l'administrateur")
|
||||||
return
|
return
|
||||||
member_obj = serialize_member(self.member)
|
member_obj = serialize_member(self.member)
|
||||||
leave_room(self.member, self.db)
|
leave_room(self.member, self.db)
|
||||||
|
self.member = None
|
||||||
await self.direct_send(type="successfully_leaved", payload={})
|
await self.direct_send(type="successfully_leaved", payload={})
|
||||||
await self.broadcast(type='leaved', payload={"member": member_obj})
|
await self.broadcast(type='leaved', payload={"member": member_obj})
|
||||||
self.member = None
|
self.member = None
|
||||||
@ -294,13 +303,13 @@ class RoomConsumer(Consumer):
|
|||||||
self.manager.remove(self.room.id, self)
|
self.manager.remove(self.room.id, self)
|
||||||
return {"waiter_id": waiter_id}
|
return {"waiter_id": waiter_id}
|
||||||
|
|
||||||
@Consumer.sending("banned", conditions=[isMember])
|
# @Consumer.sending("banned", conditions=[isMember])
|
||||||
async def banned(self):
|
# def banned(self):
|
||||||
self.member = None
|
# self.member = None
|
||||||
self.manager.remove(self.room.id_code, self)
|
# self.manager.remove(self.room.id_code, self)
|
||||||
self.banned = True
|
# self.banned = True
|
||||||
#await self.ws.close()
|
# #await self.ws.close()
|
||||||
return {}
|
# return {}
|
||||||
|
|
||||||
@Consumer.sending('ping', conditions=[isMember])
|
@Consumer.sending('ping', conditions=[isMember])
|
||||||
def ping(self):
|
def ping(self):
|
||||||
|
@ -8,30 +8,7 @@ from sqlmodel import Session, select
|
|||||||
from database.auth.models import User
|
from database.auth.models import User
|
||||||
from database.db import get_session
|
from database.db import get_session
|
||||||
from database.exercices.models import Exercice
|
from database.exercices.models import Exercice
|
||||||
from database.room.crud import serialize_parcours_short, change_correction, corrige_challenge, \
|
from database.room.crud import delete_room_db
|
||||||
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
|
|
||||||
from database.room.models import Challenge, ChallengeRead, Challenges, ParcoursReadUpdate, ChallengeInfo, Member, \
|
|
||||||
Parcours, ParcoursCreate, ParcoursRead, ParcoursReadShort, Room, RoomConnectionInfos, \
|
|
||||||
RoomCreate, RoomInfo, TmpCorrection, CorrigedData, CorrectionData
|
|
||||||
from generateur.generateur_main import generate_from_path, parseGeneratorOut
|
|
||||||
from routes.room.consumer import RoomConsumer
|
|
||||||
from routes.room.manager import RoomManager
|
|
||||||
from services.auth import get_current_user_optional
|
|
||||||
from services.io import add_fast_api_root
|
|
||||||
from services.misc import stripKeyDict
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, WebSocket, status, Query, Body
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from pydantic import BaseModel
|
|
||||||
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 serialize_parcours_short, change_correction, corrige_challenge, \
|
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, \
|
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, \
|
update_parcours_db, get_parcours, get_room, check_admin, get_exercices, get_challenge, get_correction, \
|
||||||
@ -68,6 +45,14 @@ def get_room_route(room: Room = Depends(get_room), member: Member = Depends(get_
|
|||||||
return serialize_room(room, member, db)
|
return serialize_room(room, member, db)
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
await m.broadcast({"type": "deleted"}, room.id_code)
|
||||||
|
return {"message": "ok"}
|
||||||
|
|
||||||
|
|
||||||
@router.post('/room/{room_id}/parcours', response_model=ParcoursRead)
|
@router.post('/room/{room_id}/parcours', response_model=ParcoursRead)
|
||||||
async def create_parcours(*, parcours: ParcoursCreate, room_id: str, member: Member = Depends(check_admin),
|
async def create_parcours(*, parcours: ParcoursCreate, room_id: str, member: Member = Depends(check_admin),
|
||||||
m: RoomManager = Depends(get_manager), db: Session = Depends(get_session)):
|
m: RoomManager = Depends(get_manager), db: Session = Depends(get_session)):
|
||||||
@ -258,8 +243,7 @@ async def corrige(*, correction: List[CorrigedData] = Body(), challenge: Challen
|
|||||||
@router.websocket('/ws/room/{room_id}')
|
@router.websocket('/ws/room/{room_id}')
|
||||||
async def room_ws(ws: WebSocket, room: Room | None = Depends(check_room), db: Session = Depends(get_session),
|
async def room_ws(ws: WebSocket, room: Room | None = Depends(check_room), db: Session = Depends(get_session),
|
||||||
m: RoomManager = Depends(get_manager)):
|
m: RoomManager = Depends(get_manager)):
|
||||||
if room is None:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail='Room not found')
|
|
||||||
consumer = RoomConsumer(ws=ws, room=room, manager=m, db=db)
|
consumer = RoomConsumer(ws=ws, room=room, manager=m, db=db)
|
||||||
await consumer.run()
|
await consumer.run()
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from typing import List, Callable, Any, Dict
|
|
||||||
from pydantic import validate_arguments, BaseModel
|
|
||||||
from fastapi.websockets import WebSocketDisconnect, WebSocket
|
|
||||||
from pydantic.error_wrappers import ValidationError
|
|
||||||
import inspect
|
import inspect
|
||||||
from starlette.websockets import WebSocketState
|
from typing import List, Callable, Any, Dict
|
||||||
|
|
||||||
|
from fastapi.websockets import WebSocketDisconnect, WebSocket
|
||||||
|
from pydantic import validate_arguments, BaseModel
|
||||||
|
from pydantic.error_wrappers import ValidationError
|
||||||
|
|
||||||
|
|
||||||
def make_event_decorator(eventsDict):
|
def make_event_decorator(eventsDict):
|
||||||
def _(name: str | List, conditions: List[Callable | bool] = []):
|
def _(name: str | List, conditions: List[Callable | bool] = []):
|
||||||
@ -62,7 +63,8 @@ class Consumer:
|
|||||||
#self.events: Dict[str, Callable] = {}
|
#self.events: Dict[str, Callable] = {}
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
pass
|
await self.ws.accept()
|
||||||
|
return True
|
||||||
|
|
||||||
async def validation_error_handler(self, e: ValidationError):
|
async def validation_error_handler(self, e: ValidationError):
|
||||||
errors = e.errors()
|
errors = e.errors()
|
||||||
@ -132,7 +134,9 @@ class Consumer:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
await self.connect()
|
accepted = await self.connect()
|
||||||
|
if accepted is False:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
data = await self.ws.receive_json()
|
data = await self.ws.receive_json()
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import { autoRefresh } from '../utils/utils';
|
import { autoRefresh } from "../utils/utils";
|
||||||
import {env} from '$env/dynamic/private';
|
import { env } from "$env/dynamic/public";
|
||||||
|
|
||||||
export const authInstance = axios.create({
|
export const authInstance = axios.create({
|
||||||
baseURL: `${env.API_BASE}`,
|
baseURL: `${env.PUBLIC_API_BASE}`,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
Accept: 'application/json',
|
Accept: "application/json",
|
||||||
'Access-Control-Allow-Origin': '*',
|
"Access-Control-Allow-Origin": "*"
|
||||||
//'X-CSRFToken': csrftoken != undefined ? csrftoken : '',
|
//'X-CSRFToken': csrftoken != undefined ? csrftoken : '',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
authInstance.interceptors.request.use(autoRefresh, (error) => {
|
authInstance.interceptors.request.use(autoRefresh, (error) => {
|
||||||
Promise.reject(error);
|
Promise.reject(error);
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { parse, stringify } from 'qs'
|
import { parse, stringify } from 'qs'
|
||||||
import { autoRefresh } from '../utils/utils';
|
import { autoRefresh } from '../utils/utils';
|
||||||
import {env} from '$env/dynamic/private';
|
import { env } from "$env/dynamic/public";
|
||||||
export const exoInstance = axios.create({
|
export const exoInstance = axios.create({
|
||||||
paramsSerializer:{encode:(params)=> {return parse(params, {arrayFormat:"brackets"})}, serialize: (p)=>{return stringify(p, {arrayFormat: "repeat"})}},
|
paramsSerializer:{encode:(params)=> {return parse(params, {arrayFormat:"brackets"})}, serialize: (p)=>{return stringify(p, {arrayFormat: "repeat"})}},
|
||||||
baseURL: `${env.API_BASE}`,
|
baseURL: `${env.PUBLIC_API_BASE}`,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import { autoRefresh } from '../utils/utils';
|
import { autoRefresh } from "../utils/utils";
|
||||||
import {env} from '$env/dynamic/private';
|
import { env } from "$env/dynamic/public";
|
||||||
|
|
||||||
export const roomInstance = axios.create({
|
export const roomInstance = axios.create({
|
||||||
baseURL: `${env.API_BASE}/room`,
|
baseURL: `${env.PUBLIC_API_BASE}room`,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
Accept: 'application/json',
|
Accept: "application/json",
|
||||||
'Access-Control-Allow-Origin': '*',
|
"Access-Control-Allow-Origin": "*"
|
||||||
//'X-CSRFToken': csrftoken != undefined ? csrftoken : '',
|
//'X-CSRFToken': csrftoken != undefined ? csrftoken : '',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
roomInstance.interceptors.request.use(
|
roomInstance.interceptors.request.use(
|
||||||
autoRefresh,
|
autoRefresh,
|
||||||
(error) => {
|
(error) => {
|
||||||
Promise.reject(error);
|
Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
afterNavigate(() => {
|
afterNavigate(() => {
|
||||||
open = false;
|
open = false;
|
||||||
});
|
});
|
||||||
|
$: console.log("USERNAME", $username);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav data-sveltekit-preload-data="hover" class:open>
|
<nav data-sveltekit-preload-data="hover" class:open>
|
||||||
@ -37,7 +38,8 @@
|
|||||||
<div class="icon">
|
<div class="icon">
|
||||||
<FaUser />
|
<FaUser />
|
||||||
</div>
|
</div>
|
||||||
{$username}</div>
|
{$username}
|
||||||
|
</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<div class="icon signout" title="Se déconnecter" on:click={()=>{
|
<div class="icon signout" title="Se déconnecter" on:click={()=>{
|
||||||
logout()
|
logout()
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{#each rooms as room}
|
{#each rooms as room}
|
||||||
<li>
|
<li>
|
||||||
<a href="/room/{room.id_code}">{room.name} ({room.admin ? "Administrateur" : "Member"})</a>
|
<a href="/room/{room.id_code}">{room.name} ({room.admin ? "Administrateur" : "Membre"})</a>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
import FaLock from "svelte-icons/fa/FaLock.svelte";
|
import FaLock from "svelte-icons/fa/FaLock.svelte";
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
|
import FaTimes from "svelte-icons/fa/FaTimes.svelte";
|
||||||
|
import FaCheck from "svelte-icons/fa/FaCheck.svelte";
|
||||||
|
import MdCheck from "svelte-icons/md/MdCheck.svelte";
|
||||||
|
import MdClose from "svelte-icons/md/MdClose.svelte";
|
||||||
|
|
||||||
const room: Writable<Room> = getContext("room");
|
const room: Writable<Room> = getContext("room");
|
||||||
const member: Writable<Member> = getContext("member");
|
const member: Writable<Member> = getContext("member");
|
||||||
@ -111,14 +115,20 @@
|
|||||||
class="accept"
|
class="accept"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
send('accept', { waiter_id: m.waiter_id });
|
send('accept', { waiter_id: m.waiter_id });
|
||||||
}}>Accept
|
}}
|
||||||
|
title="Accepter"
|
||||||
|
>
|
||||||
|
<MdCheck />
|
||||||
</button
|
</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="refuse"
|
class="refuse"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
send('refuse', { waiter_id: m.waiter_id });
|
send('refuse', { waiter_id: m.waiter_id });
|
||||||
}}>Refuse
|
}}
|
||||||
|
title="Refuser"
|
||||||
|
>
|
||||||
|
<MdClose />
|
||||||
</button
|
</button
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
@ -171,6 +181,9 @@
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: grey;
|
color: grey;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@ -203,4 +216,27 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.8em !important;
|
font-size: 0.8em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accept, .refuse {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
transition: .3s;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept {
|
||||||
|
margin-left: 5px;
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refuse {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
import { messages, handlers } from "../../store/ws";
|
import { messages, handlers } from "../../store/ws";
|
||||||
import Stats from "./Stats.svelte";
|
import Stats from "./Stats.svelte";
|
||||||
import ChallengesList from "./ChallengesList.svelte";
|
import ChallengesList from "./ChallengesList.svelte";
|
||||||
|
import { delParcours } from "../../requests/room.request.js";
|
||||||
|
import FaRegTrashAlt from "svelte-icons/fa/FaRegTrashAlt.svelte";
|
||||||
|
|
||||||
export let id_code: string;
|
export let id_code: string;
|
||||||
|
|
||||||
@ -20,7 +22,7 @@
|
|||||||
const member: Writable<Member> = getContext("member");
|
const member: Writable<Member> = getContext("member");
|
||||||
|
|
||||||
const { send } = getContext<{ send: Function }>("ws");
|
const { send } = getContext<{ send: Function }>("ws");
|
||||||
|
const {alert} = getContext<{alert: Function}>("alert");
|
||||||
const parcours: Writable<ParcoursRead | null> = getContext("parcours");
|
const parcours: Writable<ParcoursRead | null> = getContext("parcours");
|
||||||
|
|
||||||
let open = "";
|
let open = "";
|
||||||
@ -53,6 +55,27 @@
|
|||||||
>
|
>
|
||||||
<FaEdit />
|
<FaEdit />
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="icon back"
|
||||||
|
on:keydown={() => {}}
|
||||||
|
on:click|stopPropagation={() => {
|
||||||
|
alert({
|
||||||
|
title: 'Supprimer ?',
|
||||||
|
description: 'Voulez vous supprimer ce parcours ?',
|
||||||
|
validate: () => {
|
||||||
|
delParcours(
|
||||||
|
$room?.id_code,
|
||||||
|
$parcours.id_code,
|
||||||
|
!$member.isUser ? $member?.clientId : null
|
||||||
|
).then(()=>{
|
||||||
|
goto(`?${new URLSearchParams().toString()}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaRegTrashAlt />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -187,6 +210,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -235,6 +259,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
padding: 3px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@ -269,4 +294,7 @@
|
|||||||
.edit {
|
.edit {
|
||||||
color: $green;
|
color: $green;
|
||||||
}
|
}
|
||||||
|
h1{
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,128 +1,150 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { delParcours } from '../../requests/room.request';
|
import { getContext } from "svelte";
|
||||||
import { getContext } from 'svelte';
|
import type { Writable } from "svelte/store";
|
||||||
import FaRegTrashAlt from 'svelte-icons/fa/FaRegTrashAlt.svelte';
|
import type { Member, Room } from "../../types/room.type";
|
||||||
import type { Writable } from 'svelte/store';
|
import { goto } from "$app/navigation";
|
||||||
import type { Member, Room } from '../../types/room.type';
|
|
||||||
import { goto } from '$app/navigation';
|
const room: Writable<Room> = getContext("room");
|
||||||
import { page } from '$app/stores';
|
const member: Writable<Member> = getContext("member");
|
||||||
const room: Writable<Room> = getContext('room');
|
|
||||||
const member: Writable<Member> = getContext('member');
|
|
||||||
const { alert } = getContext<{ alert: Function }>('alert');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="parcours">
|
<div class="parcours">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<h2>Parcours</h2>
|
<h2>Parcours</h2>
|
||||||
{#if $member.isAdmin}
|
{#if $member.isAdmin}
|
||||||
<button
|
<button
|
||||||
class="primary-btn"
|
class="primary-btn"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
goto(`?${new URLSearchParams({ p: 'new' }).toString()}`);
|
goto(`?${new URLSearchParams({ p: 'new' }).toString()}`);
|
||||||
}}>Nouveau</button
|
}}>Nouveau
|
||||||
>
|
</button
|
||||||
{/if}
|
>
|
||||||
</div>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="list">
|
<div class="list">
|
||||||
{#if $room.parcours.length == 0}
|
{#if $room.parcours.length == 0}
|
||||||
<p class="empty">Aucun parcours pour le moment</p>
|
<p class="empty">Aucun parcours pour le moment</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#each $room.parcours as p}
|
{#each $room.parcours as p}
|
||||||
<div
|
<div
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
goto(`?${new URLSearchParams({ p: p.id_code }).toString()}`);
|
goto(`?${new URLSearchParams({ p: p.id_code }).toString()}`);
|
||||||
}}
|
}}
|
||||||
on:keydown={() => {}}
|
on:keydown={() => {}}
|
||||||
>
|
>
|
||||||
<p>{p.name}</p>
|
<p class="parcours-name">{p.name}</p>
|
||||||
{#if p.best_note}
|
|
||||||
<p>Record : {p.best_note} fautes</p>
|
|
||||||
{:else}
|
|
||||||
Aucun essai effectué
|
|
||||||
{/if}
|
|
||||||
{#if p.validated}
|
|
||||||
<p data-testid="valid">Parcours validé</p>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $member.isAdmin}
|
|
||||||
<div
|
<div class="stats">
|
||||||
class="icon delete"
|
<p class="stat">
|
||||||
on:keydown={() => {}}
|
{#if p.best_note}
|
||||||
on:click|stopPropagation={() => {
|
<strong>Record :</strong> {p.best_note} faute{p.best_note > 1 ? "s" : ""}
|
||||||
alert({
|
{:else}
|
||||||
title: 'Supprimer ?',
|
Aucun essai effectué
|
||||||
description: 'Voulez vous supprimer ce parcours ?',
|
{/if}
|
||||||
validate: () => {
|
</p>
|
||||||
delParcours(
|
<p class="stat valid" data-testid="valid"
|
||||||
$room?.id_code,
|
class:validated={p.validated}>{ p.validated ? "Parcours validé" : "Parcours non validé"}</p>
|
||||||
p.id_code,
|
|
||||||
!$member.isUser ? $member?.clientId : null
|
</div>
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
</div>
|
||||||
}}
|
{/each}
|
||||||
>
|
</div>
|
||||||
<FaRegTrashAlt />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.empty {
|
.empty {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
}
|
}
|
||||||
.parcours {
|
|
||||||
background-color: rgba($background, 0.4);
|
|
||||||
border: 1px solid $border;
|
|
||||||
padding: 20px;
|
.parcours {
|
||||||
width: 100%;
|
background-color: rgba($background, 0.4);
|
||||||
height: 100%;
|
border: 1px solid $border;
|
||||||
display: flex;
|
padding: 20px;
|
||||||
flex-direction: column;
|
width: 100%;
|
||||||
border-radius: 5px;
|
height: 100%;
|
||||||
}
|
display: flex;
|
||||||
.head {
|
flex-direction: column;
|
||||||
display: flex;
|
border-radius: 5px;
|
||||||
align-items: center;
|
}
|
||||||
justify-content: space-between;
|
|
||||||
border-bottom: 1px solid $border;
|
.head {
|
||||||
padding: 10px 0;
|
display: flex;
|
||||||
button {
|
align-items: center;
|
||||||
width: max-content;
|
justify-content: space-between;
|
||||||
}
|
border-bottom: 1px solid $border;
|
||||||
}
|
padding: 10px 0;
|
||||||
.icon {
|
|
||||||
width: 18px;
|
button {
|
||||||
height: 18px;
|
width: max-content;
|
||||||
color: $red;
|
}
|
||||||
transition: 0.4s;
|
}
|
||||||
opacity: 0;
|
|
||||||
&:hover {
|
.icon {
|
||||||
transform: scale(1.1);
|
width: 18px;
|
||||||
}
|
height: 18px;
|
||||||
}
|
color: $red;
|
||||||
.list {
|
transition: 0.4s;
|
||||||
overflow: auto;
|
opacity: 0;
|
||||||
> div {
|
|
||||||
padding: 30px 10px;
|
&:hover {
|
||||||
border-bottom: 1px solid $border-light;
|
transform: scale(1.1);
|
||||||
cursor: pointer;
|
}
|
||||||
transition: 0.3s;
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
.list {
|
||||||
justify-content: space-between;
|
overflow: auto;
|
||||||
&:hover {
|
|
||||||
background-color: lighten($color: $background, $amount: 10);
|
> div {
|
||||||
.icon {
|
padding: 30px 10px;
|
||||||
opacity: 1;
|
border-bottom: 1px solid $border-light;
|
||||||
}
|
cursor: pointer;
|
||||||
}
|
transition: 0.3s;
|
||||||
}
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($color: $background, $amount: 10);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid {
|
||||||
|
font-weight: 600;
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validated {
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat{
|
||||||
|
text-align: justify;
|
||||||
|
height: 1em;
|
||||||
|
margin: 5px 0;
|
||||||
|
width: max-content;
|
||||||
|
&::after{
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.parcours-name {
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,47 +1,51 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from "svelte";
|
||||||
import FaRegTrashAlt from 'svelte-icons/fa/FaRegTrashAlt.svelte';
|
import FaRegTrashAlt from "svelte-icons/fa/FaRegTrashAlt.svelte";
|
||||||
|
|
||||||
import FaUndo from 'svelte-icons/fa/FaUndo.svelte';
|
import FaUndo from "svelte-icons/fa/FaUndo.svelte";
|
||||||
import FaTimes from 'svelte-icons/fa/FaTimes.svelte';
|
import FaTimes from "svelte-icons/fa/FaTimes.svelte";
|
||||||
import { goto } from '$app/navigation';
|
import FaSignOutAlt from "svelte-icons/fa/FaSignOutAlt.svelte";
|
||||||
import type { Writable } from 'svelte/store';
|
|
||||||
import type { Room, Member } from '../../types/room.type';
|
import { goto } from "$app/navigation";
|
||||||
import type ReconnectingWebSocket from 'reconnecting-websocket';
|
import type { Writable } from "svelte/store";
|
||||||
const room: Writable<Room> = getContext('room');
|
import type { Member, Room } from "../../types/room.type";
|
||||||
const member: Writable<Member>= getContext('member');
|
import type ReconnectingWebSocket from "reconnecting-websocket";
|
||||||
const { send, ws } = getContext<{send: Function, ws: ReconnectingWebSocket}>('ws');
|
import { deleteRoom } from "../../requests/room.request.js";
|
||||||
let editing = false;
|
|
||||||
let name = $room.name;
|
const room: Writable<Room> = getContext("room");
|
||||||
let r: HTMLInputElement;
|
const member: Writable<Member> = getContext("member");
|
||||||
|
const { send, ws } = getContext<{ send: Function, ws: ReconnectingWebSocket }>("ws");
|
||||||
|
const { alert } = getContext<{ alert: Function }>("alert");
|
||||||
|
let editing = false;
|
||||||
|
let name = $room.name;
|
||||||
|
let r: HTMLInputElement;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{#if !editing}
|
{#if !editing}
|
||||||
<h1
|
<h1
|
||||||
on:dblclick={() => {
|
on:dblclick={() => {
|
||||||
if(!$member.isAdmin) return
|
if(!$member.isAdmin) return
|
||||||
editing = true;
|
editing = true;
|
||||||
name = $room.name;
|
name = $room.name;
|
||||||
r.focus();
|
r.focus();
|
||||||
console.log("OPENED")
|
console.log("OPENED")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{$room.name}<span on:dblclick|stopPropagation>#{$room.id_code}</span>
|
{$room.name}<span on:dblclick|stopPropagation>#{$room.id_code}</span>
|
||||||
</h1>
|
</h1>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
bind:this={r}
|
||||||
class="input"
|
bind:value={name}
|
||||||
class:hide={!editing}
|
class="input"
|
||||||
on:focusout={() => {
|
class:hide={!editing}
|
||||||
|
on:focusout={() => {
|
||||||
editing = false;
|
editing = false;
|
||||||
}}
|
}}
|
||||||
bind:value={name}
|
on:keydown={(e) => {
|
||||||
bind:this={r}
|
|
||||||
on:keydown={(e) => {
|
|
||||||
if (e.key == 'Escape') {
|
if (e.key == 'Escape') {
|
||||||
editing = false;
|
editing = false;
|
||||||
} else if (e.key == 'Enter') {
|
} else if (e.key == 'Enter') {
|
||||||
@ -49,102 +53,134 @@
|
|||||||
editing = false;
|
editing = false;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
type="text"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
{#if $member.isAdmin}
|
{#if $member.isAdmin}
|
||||||
<div class="icon trash" data-testid="delete"><FaRegTrashAlt /></div>
|
<div class="icon trash" data-testid="delete" on:click={()=>{
|
||||||
{/if}
|
alert({title:"Voulez vous vraiment supprimer la salle ?",
|
||||||
|
description:"L'intégralité des données de la salle sera supprimée et vous ne pourrez plus y accéder",
|
||||||
|
validate: ()=>{
|
||||||
|
deleteRoom($room.id_code, !$member.isUser ? $member.clientId: null)
|
||||||
|
},
|
||||||
|
validateButton: "Supprimer la salle"})
|
||||||
|
|
||||||
<div
|
//goto('/room/join')
|
||||||
class="icon refresh"
|
}}>
|
||||||
on:click={() => {
|
<FaRegTrashAlt />
|
||||||
console.log(ws)
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="icon trash" data-testid="leave" on:click={()=>{
|
||||||
|
alert({title:"Voulez vous vraiment quitter la salle ?",
|
||||||
|
description:"Vous perdrez l'intégralité de vos résultats et ne pourrez plus accéder à cette salle sans y avoir été accepté à nouveau",
|
||||||
|
validate: ()=>{
|
||||||
|
send('leave')
|
||||||
|
},
|
||||||
|
validateButton: "Quitter la salle"})
|
||||||
|
|
||||||
|
//goto('/room/join')
|
||||||
|
}}
|
||||||
|
on:keypress={()=>{}}
|
||||||
|
>
|
||||||
|
<FaSignOutAlt />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="icon refresh"
|
||||||
|
data-testid="refresh"
|
||||||
|
on:click={() => {
|
||||||
ws.reconnect();
|
ws.reconnect();
|
||||||
}}
|
}}
|
||||||
on:keydown={() => {}}
|
on:keydown={() => {}}
|
||||||
data-testid="refresh"
|
>
|
||||||
>
|
<FaUndo />
|
||||||
<FaUndo />
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
class="icon trash"
|
||||||
data-testid="leave"
|
data-testid="leave"
|
||||||
class="icon trash"
|
on:click={() => {
|
||||||
on:click={() => {
|
|
||||||
ws.close();
|
ws.close();
|
||||||
goto('/room/join')
|
goto('/room/join')
|
||||||
}}
|
}}
|
||||||
on:keydown={() => {}}
|
on:keydown={() => {}}
|
||||||
>
|
>
|
||||||
<FaTimes />
|
<FaTimes />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.hide {
|
.hide {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.title {
|
|
||||||
max-width: 50%;
|
|
||||||
margin: 10px 0;
|
|
||||||
font-size: 2em;
|
|
||||||
padding: 0 10px;
|
|
||||||
// height: 50px;
|
|
||||||
input {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: 700;
|
|
||||||
transition: all 0s;
|
|
||||||
transition: border 0.3s;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: inherit;
|
|
||||||
span {
|
|
||||||
font-size: 0.5em;
|
|
||||||
color: grey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.head {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
.title {
|
||||||
width: 20px;
|
max-width: 50%;
|
||||||
height: 20px;
|
margin: 10px 0;
|
||||||
transition: 0.2s;
|
font-size: 2em;
|
||||||
cursor: pointer;
|
padding: 0 10px;
|
||||||
display: flex;
|
// height: 50px;
|
||||||
&:hover {
|
input {
|
||||||
transform: scale(1.1);
|
font-size: inherit;
|
||||||
}
|
font-weight: 700;
|
||||||
}
|
transition: all 0s;
|
||||||
|
transition: border 0.3s;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.trash {
|
h1 {
|
||||||
color: $red;
|
margin: 0;
|
||||||
}
|
font-weight: 700;
|
||||||
|
font-size: inherit;
|
||||||
|
|
||||||
.refresh {
|
span {
|
||||||
color: $contrast;
|
font-size: 0.5em;
|
||||||
&:hover {
|
color: grey;
|
||||||
transform: rotate(-360deg);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icons {
|
.head {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
align-items: center;
|
||||||
align-items: center;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
transition: 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trash {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh {
|
||||||
|
color: $contrast;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,118 +1,137 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ParcoursRead, Member } from '../../types/room.type';
|
import type { ParcoursRead, Member } from "../../types/room.type";
|
||||||
import { parseTimer, statsCalculator } from '../../utils/utils';
|
import { parseTimer, statsCalculator } from "../../utils/utils";
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from "svelte";
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from "svelte/store";
|
||||||
import Classement from './Classement.svelte';
|
import Classement from "./Classement.svelte";
|
||||||
|
|
||||||
const parcours: Writable<ParcoursRead | null> = getContext('parcours');
|
const parcours: Writable<ParcoursRead | null> = getContext("parcours");
|
||||||
const member: Writable<Member | null> = getContext('member');
|
const member: Writable<Member | null> = getContext("member");
|
||||||
|
|
||||||
$: stats =
|
$: stats =
|
||||||
$parcours != null && $member != null && !!$parcours.challenges[$member.id_code]
|
$parcours != null && $member != null && !!$parcours.challenges[$member.id_code]
|
||||||
? statsCalculator($parcours.challenges[$member.id_code].challenges.map((c) => c.mistakes))
|
? statsCalculator($parcours.challenges[$member.id_code].challenges.map((c) => c.mistakes))
|
||||||
: null;
|
: null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $parcours != null}
|
{#if $parcours != null}
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<h1 class:validated={$parcours.validated}>
|
<h1 class:validated={$parcours.validated}>
|
||||||
{$parcours.validated ? 'Parcours validé !' : 'Parcours non validé !'}
|
{$parcours.validated ? 'Parcours validé !' : 'Parcours non validé !'}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="statistics">
|
<div class="statistics">
|
||||||
<p data-testid="avg">
|
<p data-testid="avg">
|
||||||
<strong>Moyenne</strong>
|
<strong>Moyenne</strong>
|
||||||
{stats?.avg.toFixed(2) !== null && stats?.avg.toFixed(2) !== undefined
|
{stats?.avg.toFixed(2) !== null && stats?.avg.toFixed(2) !== undefined
|
||||||
? `${stats?.avg.toFixed(2)} fautes`
|
? `${stats?.avg.toFixed(2)} fautes`
|
||||||
: '-'}
|
: '-'}
|
||||||
</p>
|
</p>
|
||||||
<p data-testid="max">
|
<p data-testid="max">
|
||||||
<strong>Pire</strong>
|
<strong>Pire</strong>
|
||||||
{stats?.max !== null && stats?.max !== undefined ? `${stats?.max} fautes` : '-'}
|
{stats?.max !== null && stats?.max !== undefined ? `${stats?.max} fautes` : '-'}
|
||||||
</p>
|
</p>
|
||||||
<p data-testid="min">
|
<p data-testid="min">
|
||||||
<strong>Meilleur</strong>
|
<strong>Meilleur</strong>
|
||||||
{stats?.min !== null && stats?.min !== undefined ? `${stats?.min} fautes` : '-'}
|
{stats?.min !== null && stats?.min !== undefined ? `${stats?.min} fautes` : '-'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="classement">
|
<div class="classement">
|
||||||
<h2>Classements</h2>
|
<h2>Classements</h2>
|
||||||
<div class="ranks">
|
<div class="ranks">
|
||||||
<div>
|
<div>
|
||||||
<h3>Top élèves</h3>
|
<h3>Top élèves</h3>
|
||||||
<Classement
|
<Classement
|
||||||
tops={$parcours.ranking.map((t) => {
|
tops={$parcours.ranking.map((t) => {
|
||||||
return { name: t.name, value: `Moyenne : ${t.avg.toFixed(2)} fautes` };
|
return { name: t.name, value: `Moyenne : ${t.avg.toFixed(2)} fautes` };
|
||||||
})}
|
})}
|
||||||
rank={$parcours.memberRank != null && $parcours.memberRank > 3 && $member != null
|
rank={$parcours.memberRank != null && $parcours.memberRank > 3 && $member != null
|
||||||
? {
|
? {
|
||||||
name: $member.username,
|
name: $member.username,
|
||||||
rank: $parcours.memberRank,
|
rank: $parcours.memberRank,
|
||||||
value: `Moyenne : ${stats?.avg.toFixed(2)} fautes`
|
value: `Moyenne : ${stats?.avg.toFixed(2)} fautes`
|
||||||
}
|
}
|
||||||
: null}
|
: null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 style:text-align="right">Top essais</h3>
|
<h3 style:text-align="right">Top essais</h3>
|
||||||
<Classement
|
<Classement
|
||||||
tops={$parcours.tops.map((t) => {
|
tops={$parcours.tops.map((t) => {
|
||||||
return {
|
return {
|
||||||
name: t.challenger.name,
|
name: t.challenger.name,
|
||||||
value: `${t.mistakes} fautes en ${parseTimer(t.time)}`
|
value: `${t.mistakes} fautes en ${parseTimer(t.time)}`
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
rank={$parcours.rank != null && $parcours.rank > 3 && $member != null
|
rank={$parcours.rank != null && $parcours.rank > 3 && $member != null
|
||||||
? {
|
? {
|
||||||
name: $member.username,
|
name: $member.username,
|
||||||
rank: $parcours.rank,
|
rank: $parcours.rank,
|
||||||
value: `${$parcours.pb.mistakes} fautes en ${parseTimer($parcours.pb.time)}`
|
value: `${$parcours.pb.mistakes} fautes en ${parseTimer($parcours.pb.time)}`
|
||||||
}
|
}
|
||||||
: null}
|
: null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.stats {
|
@import "../../mixins.scss";
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
.statistics {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
p {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
.stats {
|
||||||
font-size: 3em;
|
display: flex;
|
||||||
color: $red;
|
flex-direction: column;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
}
|
width: 100%;
|
||||||
.validated {
|
gap: 20px;
|
||||||
color: $green;
|
}
|
||||||
}
|
|
||||||
.ranks {
|
.statistics {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-evenly;
|
gap: 20px;
|
||||||
}
|
|
||||||
.classement {
|
p {
|
||||||
width: 100%;
|
display: flex;
|
||||||
h2{
|
flex-direction: column;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3em;
|
||||||
|
color: $red;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validated {
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranks {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
@include down(750) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.classement {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,7 +4,7 @@ import {authInstance} from '../apis/auth.api';
|
|||||||
export const loginRequest = (data: { username: string; password: string }) => {
|
export const loginRequest = (data: { username: string; password: string }) => {
|
||||||
return authInstance
|
return authInstance
|
||||||
.request({
|
.request({
|
||||||
url: 'http://localhost:8002/login',
|
url: '/login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
headers: {
|
headers: {
|
||||||
@ -23,7 +23,7 @@ export const registerRequest = (data: {
|
|||||||
}) => {
|
}) => {
|
||||||
return authInstance
|
return authInstance
|
||||||
.request({
|
.request({
|
||||||
url: 'http://localhost:8002/register',
|
url: '/register',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
headers: {
|
headers: {
|
||||||
@ -39,7 +39,7 @@ export const registerRequest = (data: {
|
|||||||
export const refreshRequest = (token: string) => {
|
export const refreshRequest = (token: string) => {
|
||||||
return authInstance
|
return authInstance
|
||||||
.request({
|
.request({
|
||||||
url: 'http://localhost:8002/refresh',
|
url: '/refresh',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
@ -13,7 +13,6 @@ export const createRoom = (data: { name: string }, username: string | null = nul
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getRoom = (id_code: string, clientId: string | null = null) => {
|
export const getRoom = (id_code: string, clientId: string | null = null) => {
|
||||||
console.log('GETROOM', clientId, { ...(clientId != null && { clientId }) });
|
|
||||||
return roomInstance
|
return roomInstance
|
||||||
.request({
|
.request({
|
||||||
url: '/' + id_code,
|
url: '/' + id_code,
|
||||||
@ -23,6 +22,16 @@ export const getRoom = (id_code: string, clientId: string | null = null) => {
|
|||||||
.then((r) => r.data);
|
.then((r) => r.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteRoom = (id_code: string, clientId: string | null = null) => {
|
||||||
|
return roomInstance
|
||||||
|
.request({
|
||||||
|
url: '/' + id_code,
|
||||||
|
method: 'DELETE',
|
||||||
|
params: { ...(clientId != null && { clientId }) }
|
||||||
|
})
|
||||||
|
.then((r) => r.data);
|
||||||
|
};
|
||||||
|
|
||||||
export const createParcours = (
|
export const createParcours = (
|
||||||
id_code: string,
|
id_code: string,
|
||||||
parcours: { time: number; name: string; max_mistakes: number; exercices: {exercice_id: string, quantity:number}[] },
|
parcours: { time: number; name: string; max_mistakes: number; exercices: {exercice_id: string, quantity:number}[] },
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
<Auth>
|
<Auth>
|
||||||
<Alert>
|
<Alert>
|
||||||
<Modal>
|
<Modal>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<NavBar/>
|
<NavBar/>
|
||||||
<slot/>
|
<slot/>
|
||||||
|
@ -26,13 +26,13 @@
|
|||||||
const room = writable<Room | null>(null);
|
const room = writable<Room | null>(null);
|
||||||
const member = writable<Member | null>(null);
|
const member = writable<Member | null>(null);
|
||||||
const parcours = writable<ParcoursRead | null>(null);
|
const parcours = writable<ParcoursRead | null>(null);
|
||||||
const ws = connect("ws://127.0.0.1:8002/ws/room/" + $page.params.slug);
|
const ws = connect("ws://127.0.0.1:8002/api/ws/room/" + $page.params.slug);
|
||||||
|
|
||||||
setContext("room", room);
|
setContext("room", room);
|
||||||
setContext("member", member);
|
setContext("member", member);
|
||||||
setContext("parcours", parcours);
|
setContext("parcours", parcours);
|
||||||
setContext("ws", { send: (type: string, data: Object) => ws.send({ type, data }), ws: ws.ws });
|
setContext("ws", { send: (type: string, data: Object) => ws.send({ type, data }), ws: ws.ws });
|
||||||
const { error } = getContext("notif");
|
const { error, info } = getContext("notif");
|
||||||
const onMessage = (payload: { type: string; data: any }) => {
|
const onMessage = (payload: { type: string; data: any }) => {
|
||||||
if (payload == undefined) return;
|
if (payload == undefined) return;
|
||||||
const { type, data } = payload;
|
const { type, data } = payload;
|
||||||
@ -83,11 +83,13 @@
|
|||||||
|
|
||||||
|
|
||||||
case "waiting":
|
case "waiting":
|
||||||
|
close()
|
||||||
$member = { ...data.waiter, room: data.room };
|
$member = { ...data.waiter, room: data.room };
|
||||||
goto(`?${new URLSearchParams({ a: "waiting" })}`);
|
goto(`?${new URLSearchParams({ a: "waiting" })}`);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "refused":
|
case "refused":
|
||||||
|
error("Refusé", "L'administrateur a refusé votre demande");
|
||||||
close();
|
close();
|
||||||
ws?.close(1000);
|
ws?.close(1000);
|
||||||
goto("/room/join");
|
goto("/room/join");
|
||||||
@ -107,11 +109,18 @@
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case "banned":
|
case "banned":
|
||||||
|
error("Ban", "Vous avez été banni de la salle par l'administrateur");
|
||||||
ws?.close(1000);
|
ws?.close(1000);
|
||||||
goto("/room/join");
|
goto("/room/join");
|
||||||
sessionStorage.removeItem("reconnect");
|
sessionStorage.removeItem("reconnect");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case "deleted":
|
||||||
|
info("Suppression", "La salle a été supprimée par l'administrateur");
|
||||||
|
ws.close(1000)
|
||||||
|
goto("/room/join")
|
||||||
|
return
|
||||||
|
|
||||||
case "error":
|
case "error":
|
||||||
const { code, msg } = data;
|
const { code, msg } = data;
|
||||||
if (code == 401) {
|
if (code == 401) {
|
||||||
@ -148,6 +157,10 @@
|
|||||||
} else {
|
} else {
|
||||||
error("Erreur", "Message : " + msg);
|
error("Erreur", "Message : " + msg);
|
||||||
}
|
}
|
||||||
|
if(code == 404){
|
||||||
|
ws.close(1000)
|
||||||
|
goto("/room/join")
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case "waiter":
|
case "waiter":
|
||||||
|
|
||||||
@ -507,6 +520,7 @@
|
|||||||
column-gap: 50px;
|
column-gap: 50px;
|
||||||
row-gap: 10px;
|
row-gap: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 7px 15px;
|
||||||
@include down(800) {
|
@include down(800) {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
Loading…
Reference in New Issue
Block a user