diff --git a/geneit_app/src/api/FamilyApi.ts b/geneit_app/src/api/FamilyApi.ts index a79e99e..0b6e5d2 100644 --- a/geneit_app/src/api/FamilyApi.ts +++ b/geneit_app/src/api/FamilyApi.ts @@ -2,6 +2,14 @@ import { APIClient } from "./ApiClient"; export interface Family {} +export enum JoinFamilyResult { + TooManyRequests, + InvalidCode, + AlreadyMember, + Error, + Success, +} + export class FamilyApi { /** * Create a new family @@ -13,4 +21,29 @@ export class FamilyApi { jsonData: { name: name }, }); } + + /** + * Join an existing family + */ + static async JoinFamily(code: string): Promise { + const res = await APIClient.exec({ + method: "POST", + uri: "/family/join", + allowFail: true, + jsonData: { code: code }, + }); + + if (res.status >= 200 && res.status < 300) return JoinFamilyResult.Success; + + switch (res.status) { + case 429: + return JoinFamilyResult.TooManyRequests; + case 404: + return JoinFamilyResult.InvalidCode; + case 409: + return JoinFamilyResult.AlreadyMember; + default: + return JoinFamilyResult.Error; + } + } } diff --git a/geneit_app/src/api/ServerApi.ts b/geneit_app/src/api/ServerApi.ts index 274dcc5..129f008 100644 --- a/geneit_app/src/api/ServerApi.ts +++ b/geneit_app/src/api/ServerApi.ts @@ -10,6 +10,7 @@ interface Constraints { user_name_len: LenConstraint; password_len: LenConstraint; family_name_len: LenConstraint; + invitation_code_len: LenConstraint; } interface OIDCProvider { diff --git a/geneit_app/src/dialogs/CreateFamilyDialog.tsx b/geneit_app/src/dialogs/CreateFamilyDialog.tsx index 6f66fa2..bd6eb44 100644 --- a/geneit_app/src/dialogs/CreateFamilyDialog.tsx +++ b/geneit_app/src/dialogs/CreateFamilyDialog.tsx @@ -39,7 +39,7 @@ export function CreateFamilyDialog(p: { return ( void; + onJoined: () => void; +}): React.ReactElement { + const [code, setCode] = React.useState(""); + const [joining, setJoining] = React.useState(false); + const [error, setError] = React.useState(); + + const alert = useAlert(); + + const cancel = () => { + setCode(""); + setJoining(false); + setError(undefined); + p.onClose(); + }; + + const joinFamily = async () => { + setJoining(true); + setError(undefined); + try { + const res = await FamilyApi.JoinFamily(code); + + switch (res) { + case JoinFamilyResult.Success: + setCode(""); + p.onJoined(); + await alert.alert("La famille a été rejointe avec succès !"); + break; + + case JoinFamilyResult.TooManyRequests: + setError("Trop de tentatives, veuillez réessayer ultérieurement..."); + break; + + case JoinFamilyResult.InvalidCode: + setError("Le code spécifié est invalide !"); + break; + + case JoinFamilyResult.AlreadyMember: + setError("Vous êtes déjà membre de cette famille !"); + break; + + case JoinFamilyResult.Error: + setError("Erreur lors de la tentative de jonction à la famille !"); + break; + } + } catch (e) { + console.error(e); + setError("Echec de l'appel au serveur !"); + } + setJoining(false); + }; + + return ( + + ); +} diff --git a/geneit_app/src/routes/FamiliesListRoute.tsx b/geneit_app/src/routes/FamiliesListRoute.tsx index 772dd6f..721221b 100644 --- a/geneit_app/src/routes/FamiliesListRoute.tsx +++ b/geneit_app/src/routes/FamiliesListRoute.tsx @@ -3,6 +3,7 @@ import { AsyncWidget } from "../widgets/AsyncWidget"; import { Family } from "../api/FamilyApi"; import { Button, Typography } from "@mui/material"; import { CreateFamilyDialog } from "../dialogs/CreateFamilyDialog"; +import { JoinFamilyDialog } from "../dialogs/JoinFamilyDialog"; export function FamiliesListRoute(): React.ReactElement { const loadKey = React.useRef(1); @@ -10,6 +11,7 @@ export function FamiliesListRoute(): React.ReactElement { const [families, setFamilies] = React.useState(null); const [createFamily, setCreateFamily] = React.useState(false); + const [joinFamily, setJoinFamily] = React.useState(false); const load = async () => { // TODO : implement @@ -24,6 +26,10 @@ export function FamiliesListRoute(): React.ReactElement { setCreateFamily(true); }; + const onRequestJoinFamily = async () => { + setJoinFamily(true); + }; + return ( ( <> {families!!.length === 0 ? ( - + ) : ( )} @@ -46,6 +55,16 @@ export function FamiliesListRoute(): React.ReactElement { reload(); }} /> + + {/** Join family dialog anchor */} + setJoinFamily(false)} + onJoined={() => { + setJoinFamily(false); + reload(); + }} + /> )} /> @@ -54,6 +73,7 @@ export function FamiliesListRoute(): React.ReactElement { function NoFamilyWidget(p: { onRequestCreateFamily: () => void; + onRequestJoinFamily: () => void; }): React.ReactElement { return (
@@ -74,7 +94,10 @@ function NoFamilyWidget(p: { onClick={p.onRequestCreateFamily} /> - {}} /> +
); diff --git a/geneit_backend/src/constants.rs b/geneit_backend/src/constants.rs index 82b736f..8429909 100644 --- a/geneit_backend/src/constants.rs +++ b/geneit_backend/src/constants.rs @@ -23,6 +23,7 @@ pub struct StaticConstraints { pub user_name_len: SizeConstraint, pub password_len: SizeConstraint, pub family_name_len: SizeConstraint, + pub invitation_code_len: SizeConstraint, } impl Default for StaticConstraints { @@ -32,6 +33,10 @@ impl Default for StaticConstraints { user_name_len: SizeConstraint::new(3, 30), password_len: SizeConstraint::new(8, 255), family_name_len: SizeConstraint::new(3, 30), + invitation_code_len: SizeConstraint::new( + FAMILY_INVITATION_CODE_LEN, + FAMILY_INVITATION_CODE_LEN, + ), } } }