Skip to content

Commit

Permalink
Regsiter user info for later use and some refactoring (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
abarghoud authored Feb 8, 2025
1 parent 07dc157 commit db20c14
Show file tree
Hide file tree
Showing 61 changed files with 1,356 additions and 558 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { PPSProfileDto } from './pps-profile-dto.model';
import { Gender } from '../../gender.enum';
import { DateTime } from 'luxon';

import { Gender } from '@pps-easy/user/domain';

import { PPSProfileDto } from './pps-profile-dto.model';

export class PPSProfileDTOToRunnerPersonalInfos {
public readonly birthdayDay: number;
public readonly birthdayMonth: number;
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/pps/domain/pps-profile-dto.model.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IsDate, IsEmail, IsEnum, IsString, MaxDate, MinDate } from 'class-validator';
import { Type } from 'class-transformer';

import { Gender } from '../../gender.enum';
import { DateTime } from 'luxon';

import { Gender } from '@pps-easy/user/domain';

export class PPSProfileDto {
@Type(() => Date)
@IsDate()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Gender } from '@pps-easy/user/domain';

import { getDefaultPPSFormData, PPSApiFormDataGenerator } from './pps-api-form-data-generator';
import { PPSProfileDTOToRunnerPersonalInfos } from '../domain/pps-profile-dto-to-runner-personal-infos';
import { PPSProfileDto } from '../domain/pps-profile-dto.model';
import { Gender } from '../../gender.enum';


const getFakePPSDto = (): PPSProfileDto => {
const ppsDto = new PPSProfileDto();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Gender } from '@pps-easy/user/domain';

export interface GeneratePPSPayload {
birthday: string;
email: string;
event_date: string;
firstname: string;
gender: 'male' | 'female';
gender: Gender;
lastname: string;
}

Expand Down
118 changes: 76 additions & 42 deletions apps/client/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,86 @@
import { Routes, Route } from "react-router-dom";
import { Route, Routes } from 'react-router-dom';
import axios from 'axios';

import { useTheme } from "@pps-easy/ui/theme-provider";
import { ClientRecaptchaChecker, GoogleRecaptchaGenerator, LocalRecaptchaChecker } from '@pps-easy/recaptcha/google';
import { useTheme } from '@pps-easy/ui/theme-provider';
import {
ClientRecaptchaChecker,
GoogleRecaptchaGenerator,
LocalRecaptchaChecker,
} from '@pps-easy/recaptcha/google';

import { MainLayout } from "./layout/MainLayout";
import { AuthLayout } from "./layout/AuthLayout";
import { LoginFormPage } from "./components/form/LoginFormPage";
import { routesConfig } from "./routes/route";
import { PrivateRoute } from "./routes/components/PrivateRoute";
import { GuestPage } from "./components/GuestPage";
import { useAuth } from "./hooks/useAuth";
import { MainLayout } from './layout/MainLayout';
import { AuthLayout } from './layout/AuthLayout';
import { LoginFormPage } from './components/form/LoginFormPage';
import { routesConfig } from './routes/route';
import { PrivateRoute } from './routes/components/PrivateRoute';
import { GuestPage } from './components/GuestPage';
import { useAuth } from './hooks/useAuth';
import { AuthenticationContext } from './contexts/authentication-context';
import { FirebaseAuthenticationService } from './service/firebase-authentication-service';
import { PPSCertificateApiContext } from './contexts/pps-certificate-api-context';
import { PPSCertificateApiService } from './api/pps-certificate-api-service';
import { IRecaptchaChecker, IRecaptchaGenerator } from '@pps-easy/recaptcha/contracts';
import {
IRecaptchaChecker,
IRecaptchaGenerator,
} from '@pps-easy/recaptcha/contracts';
import { LocalRecaptchaGenerator } from './service/local-recaptcha-generator';
import { LocalAuthenticationService } from './service/local-authentication-service';
import { ContactFormApiServiceContext } from './contexts/contact-form-api-context';
import { LocalContactFormApiService } from './api/local-contact-form-api-service';
import { FirebaseContactFormApiService } from './api/firebase-contact-form-api-service';
import { AuthenticationUserPersistenceHandler } from './service/authentication-user-persistence/authentication-user-persistence-handler';
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { FirebaseAuthenticationUserPersistenceRepository } from './service/authentication-user-persistence/firebase-authentication-user-persistence-repository';
import {
LocalstorageAuthenticationUserPersistenceStateTracker
} from './service/authentication-user-persistence/localstorage-authentication-user-persistence-state-tracker';
import {
DoNothingUserPersistenceHandler
} from './service/authentication-user-persistence/do-nothing-user-persistence-handler';
import { LocalstorageAuthenticationUserPersistenceStateTracker } from './service/authentication-user-persistence/localstorage-authentication-user-persistence-state-tracker';
import { DoNothingUserPersistenceHandler } from './service/authentication-user-persistence/do-nothing-user-persistence-handler';
import { EventFormService } from './service/event-form-service';
import { EventFormServiceContext } from './contexts/event-form-service-context';
import { UserPersonalInfoFirebaseRepository } from '@pps-easy/user/data-access';
import { initDb } from './config/firebase';

export function App() {
const { theme } = useTheme();
const { loading } = useAuth();

const axiosInstance = axios.create({
baseURL: process.env.API_URL || 'http://localhost:3000',
});
const isLocalEnvironment = process.env.ENVIRONMENT === 'local';
const googleRecaptchaGenerator: IRecaptchaGenerator = isLocalEnvironment ?
new LocalRecaptchaGenerator() :
new GoogleRecaptchaGenerator(process.env.REACT_APP_RECAPTCHA_SITE_KEY || '');
const clientRecaptchaChecker: IRecaptchaChecker = isLocalEnvironment ?
new LocalRecaptchaChecker() :
new ClientRecaptchaChecker(axiosInstance);
const authenticationService = isLocalEnvironment ?
new LocalAuthenticationService() :
new FirebaseAuthenticationService(clientRecaptchaChecker, googleRecaptchaGenerator);
const contactFormApiService = isLocalEnvironment ?
new LocalContactFormApiService() :
new FirebaseContactFormApiService();
const ppsCertificateApiService = new PPSCertificateApiService(axiosInstance, googleRecaptchaGenerator)
const userUpdater = isLocalEnvironment ? new DoNothingUserPersistenceHandler() : new AuthenticationUserPersistenceHandler(
authenticationService,
new FirebaseAuthenticationUserPersistenceRepository(),
new LocalstorageAuthenticationUserPersistenceStateTracker()
const googleRecaptchaGenerator: IRecaptchaGenerator = isLocalEnvironment
? new LocalRecaptchaGenerator()
: new GoogleRecaptchaGenerator(
process.env.REACT_APP_RECAPTCHA_SITE_KEY || ''
);
const clientRecaptchaChecker: IRecaptchaChecker = isLocalEnvironment
? new LocalRecaptchaChecker()
: new ClientRecaptchaChecker(axiosInstance);
const authenticationService = isLocalEnvironment
? new LocalAuthenticationService()
: new FirebaseAuthenticationService(
clientRecaptchaChecker,
googleRecaptchaGenerator
);
const contactFormApiService = isLocalEnvironment
? new LocalContactFormApiService()
: new FirebaseContactFormApiService();
const ppsCertificateApiService = new PPSCertificateApiService(
axiosInstance,
googleRecaptchaGenerator
);
const userUpdater = isLocalEnvironment
? new DoNothingUserPersistenceHandler()
: new AuthenticationUserPersistenceHandler(
authenticationService,
new FirebaseAuthenticationUserPersistenceRepository(),
new LocalstorageAuthenticationUserPersistenceStateTracker()
);
const personalInfoRepository = useMemo(
() => new UserPersonalInfoFirebaseRepository(initDb(), authenticationService),
[authenticationService]
);
const eventFormService = useMemo(
() =>
new EventFormService(ppsCertificateApiService, personalInfoRepository),
[personalInfoRepository, ppsCertificateApiService]
);

useEffect(() => {
Expand All @@ -63,11 +89,15 @@ export function App() {

return (
<AuthenticationContext.Provider value={authenticationService}>
<PPSCertificateApiContext.Provider value={ppsCertificateApiService}>
<EventFormServiceContext.Provider value={eventFormService}>
<ContactFormApiServiceContext.Provider value={contactFormApiService}>
<div className={`
min-h-screen flex flex-col justify-center ${theme === "dark" ? "bg-background dark:bg-background" : "bg-white"} ${loading ? "items-center" : ""}
`}>
<div
className={`
min-h-screen flex flex-col justify-center ${
theme === 'dark' ? 'bg-background dark:bg-background' : 'bg-white'
} ${loading ? 'items-center' : ''}
`}
>
<Routes>
<Route path="/login" element={<AuthLayout />}>
<Route path="" element={<LoginFormPage />} />
Expand All @@ -78,14 +108,18 @@ export function App() {
<Route element={<PrivateRoute />}>
<Route element={<MainLayout />}>
{routesConfig.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
<Route
key={route.path}
path={route.path}
element={route.element}
/>
))}
</Route>
</Route>
</Routes>
</div>
</ContactFormApiServiceContext.Provider>
</PPSCertificateApiContext.Provider>
</EventFormServiceContext.Provider>
</AuthenticationContext.Provider>
);
}
Expand Down
88 changes: 66 additions & 22 deletions apps/client/src/app/components/form/EventForm.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,125 @@
import { FC } from 'react';
import React, { FC, useMemo } from 'react';
import { FormProvider, UseFormReturn } from 'react-hook-form';
import { Card, CardContent, CardHeader, CardTitle } from '@pps-easy/ui/card';
import { Button } from '@pps-easy/ui/button';
import { EventFormInputField, EventFormSelectField } from './EventFormFields';
import { FormValues } from '../../schema/event-form-schema';
import { EventFormValues } from '../../schema/event-form-schema';
import { addMonths, format } from 'date-fns';
import { Checkbox } from '@pps-easy/ui/checkbox';
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from '@pps-easy/ui/form';
import { Gender } from '@pps-easy/user/domain';

interface EventFormProps {
formMethods: UseFormReturn<FormValues>;
form: UseFormReturn<EventFormValues>;
isSubmitting: boolean;
onSubmit: (data: FormValues) => void;
onSubmit: (data: EventFormValues) => void;
shouldDisplaySaveForLaterUseField: boolean | undefined;
}

export const EventForm: FC<EventFormProps> = ({
formMethods,
form,
isSubmitting,
onSubmit
onSubmit,
shouldDisplaySaveForLaterUseField,
}) => {
const today = new Date();
const minRaceDate = format(today, 'yyyy-MM-dd');
const maxRaceDate = format(addMonths(today, 3), 'yyyy-MM-dd');

return (
<FormProvider {...formMethods}>
<Card className="w-full max-w-4xl mx-auto min-h-[595px] flex flex-col p-6 bg-card rounded-lg shadow-2xl border-2 border-border overflow-auto">
<CardHeader className='mb-8'>
<CardTitle className="text-3xl font-bold text-center text-primary">PPS Easy - Nouveau Certificat</CardTitle>
<FormProvider {...form}>
<Card
className="w-full max-w-4xl mx-auto min-h-[595px] flex flex-col p-6 bg-card rounded-lg shadow-2xl border-2 border-border overflow-auto">
<CardHeader className="mb-8">
<CardTitle className="text-3xl font-bold text-center text-primary">
PPS Easy - Nouveau Certificat
</CardTitle>
</CardHeader>
<CardContent className="text-foreground mt-8">
<form onSubmit={formMethods.handleSubmit(onSubmit)} className="space-y-6 flex-grow">
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 flex-grow"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<EventFormInputField
control={formMethods.control}
type='date'
control={form.control}
type="date"
label="Date de naissance"
name="birthday"
placeholder="JJ/MM/AAAA"
/>
<EventFormInputField
control={formMethods.control}
type='date'
control={form.control}
type="date"
label="Date de la course"
max={maxRaceDate}
min={minRaceDate}
name="eventDate"
placeholder="JJ/MM/AAAA"
/>
<EventFormInputField
control={formMethods.control}
control={form.control}
label="Email"
name="email"
placeholder="[email protected]"
/>
<EventFormInputField
control={formMethods.control}
control={form.control}
label="Prénom"
name="firstname"
placeholder="Prénom"
/>
<EventFormInputField
control={formMethods.control}
control={form.control}
label="Nom"
name="lastname"
placeholder="Nom"
/>
<EventFormSelectField
control={formMethods.control}
control={form.control}
label="Genre"
name="gender"
options={[
{ value: 'homme', label: 'Homme' },
{ value: 'femme', label: 'Femme' },
{ value: Gender.male, label: 'Homme' },
{ value: Gender.female, label: 'Femme' }
]}
placeholder="Sélectionnez votre genre"
/>

{
shouldDisplaySaveForLaterUseField ?
<FormField
control={form.control}
name="saveForLaterUse"
render={({ field }) => (
<FormItem
className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4 shadow col-span-1 md:col-span-2">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
Sauvegarder mes informations pour la prochaine fois
</FormLabel>
<FormDescription>
À la prochaine connexion, vos informations seront pré-remplies, vous n'aurez qu'à remplir la
date de la course.
</FormDescription>
</div>
</FormItem>
)}
/> :
null
}
</div>
<Button className="w-full bg-primary text-white" type="submit" isLoading={isSubmitting}>
<Button
className="w-full bg-primary text-white"
type="submit"
isLoading={isSubmitting}
>
Générer votre certificat
</Button>
</form>
Expand Down
Loading

0 comments on commit db20c14

Please sign in to comment.