Retour aux vidéos

On développe une app d'échanges de services

Dans cette vidéo, nous allons ajouter des fonctionnalités à notre app : - Création d'une proposition de service (tondre la pelouse) - Lister les services proposés - Faire appel au service (en faisant une offre au prestataire) - Proposer un prix - Pouvoir accepter ou refuser le prix - Éditer le profil utilisateur

00:00 Dans cette vidéo, nous allons enfin rajouter des fonctionnalités à notre application. C'est le sixième épisode de notre série où l'on développe une application de mise en relation avec Remix. Jusqu'à présent, nous avons ajouté des fonctionnalités nécessaires, mais pas du tout fun. On parle là d'authentification, de design système, de DevOps et toute la configuration du projet. Maintenant, on peut s'amuser à développer notre application et c'est ce que nous allons faire dès maintenant.

00:27 Allons-y. Le projet Coup de Pouce était parti d'une idée simple. Quand tu es étudiant et tu as besoin par exemple d'une visseuse ou d'un tournevis ou de matériel de bricolage, tu as deux choix, soit tu vas aller l'acheter en magasin, soit tu vas demander à ton voisin de te dépanner. C'était un petit peu ça l'idée de cette application et on va donc tout de suite commencer à créer nos premiers features. On va d'abord commencer par remplir ces écrans-là ici, donc on a une barre de recherche, on a un filtre par catégorie et on a des types de catégories et des types d'annonces.

00:57 Donc la première chose que je t'invite à faire, c'est d'aller sur ce repository et de le télécharger si ce n'est pas déjà fait ici. Tu vas donc ouvrir un nouveau terminal et tu vas aller dans le dossier dans lequel tu fais souvent tes développements et tu vas faire un guide clone de ce repository là comme ça et maintenant tu vas faire un cd à l'intérieur comme ceci et tu vas faire un check out sur la branche zéro quatre tiret authentification qui est la dernière branche sur lequel on a bossé. Et maintenant, à partir de cette branche, tu vas faire un git check out tiret b et tu vas écrire par exemple zéro cinq tiret build date app. On a vraiment très envie de build. Et maintenant, tu vas l'ouvrir avec VS Code.

01:33 Une fois dans VS Code, on va d'abord faire un NPM install comme ceci, puis nous allons lancer l'application avec un NPM run dev. Et on a cette erreur dans le terminal parce qu'on a besoin de lancer notre base de données Redis. Heureusement, on a un fichier pour ça qui s'appelle docker compose point redis ici qu'on va exécuter dans un deuxième terminal en tapant la commande docker compose tiret s docker tiret compose point redis point YML qui est le nom du fichier et on va rajouter un up et un tiret d comme ceci. On n'a pas besoin de build cette image à nouveau, elle a déjà été bulgare. Pour que cette commande marche, on a besoin d'avoir l'application docker d'installer et d'exécuter.

02:09 On peut voir ici que j'ai eu une erreur, erreur Respande from themele driver fail. Et ça les amis, c'est parce que je n'avais pas installé cette application. Donc là, ce qu'on va faire, c'est qu'on va ouvrir l'application Docker, qu'il est possible très facilement d'installer et ensuite, maintenant qu'elle est ouverte, on va arrêter toutes les applis ici, celle-ci et celle-ci. On peut voir qu'on avait déjà une instance release, et ce qu'on va faire maintenant, c'est qu'on va réexécuter cette commande docker compost. Si on retourne sur le client docker, on peut voir que l'instance est en train de tourner et on peut s'y connecter.

02:38 Maintenant, on va pouvoir fermer ce terminal et on peut voir que le message d'erreur dans le premier terminal a disparu. On a maintenant été connecté à notre base de données Redis. Maintenant, on va ouvrir notre application sur locallost trois-mille essais pour voir un petit peu où l'on en est et on va se connecter avec notre utilisateur ABC un, deux, trois. Une fois connecté, on arrive sur cet écran. On peut voir qu'il n'y a rien d'intéressant sur cette page.

03:01 Et ce qu'on va faire, c'est qu'on va commencer à intégrer cet écran-là. On ne va pas encore ajouter la catégorie et les filtres. On va commencer par ajouter les annonces, le titre, le prix, le libellé, le lieu et la publication. On va donc s'endre dans le fichier index point TFX qui commence par un underscore ici. On peut retrouver le fichier underscore index point TFX à cet endroit-là, donc dans notre application frontend, dans le dossier app et dans le dossier route, parce que c'est une route de remix, c'est la route de la page d'accueil et c'est à cet endroit qu'on va créer notre composant réutilisable d'annonces.

03:33 Donc pour ce faire, on va créer par exemple une offer card avec donc une proposition, une offre de service. On pourrait même appeler ça service card plutôt et ça va donc renvoyer un div qui va être en flex call comme ceci et ça va potentiellement contenir par exemple une image, le nom du service, une description. On va quand même s'inspirer du figma ici, on va copier cet ordre-là. Donc on va commencer par le titre ici qui est donc un title. On peut commencer à utiliser des variables, par exemple tondeuse à gazon.

04:04 Ensuite, il va y avoir le prix dix euros le l'heure par exemple. Ensuite, le libellé, on va appeler ça description. Ça va être, voilà, je propose mes services pour tondre votre boulot, c'est très très bien ça. Ensuite, on aura donc le lieu, l'adresse, on va mettre Paris. Effectivement, c'est une bonne suggestion et l'heure de publication.

04:21 Pour l'heure, on peut prendre une date date oblige est égal à, voilà, on va utiliser cette date pour le moment. On va faire rien de compliqué et maintenant, on va afficher quelques cartes comme celle-ci dans notre composant index. Ici. On va appeler notre carte et on va utiliser pour le moment un grid calls deux comme ceci pour afficher notre image. Et on va bien sûr rajouter du gap, gap de huit par exemple pour commencer, ça sera très bien.

04:45 Ok voilà, donc là on a un petit peu amélioré notre carte. C'est pas très beau, mais ça va faire l'affaire pour le moment. On a donc le prix, le nom du service, la description par le prestataire, le lieu et la date de publication. Pour le moment, cette donnée est codée en dur et on va très bientôt la sauvegarder en base de données. On va maintenant rajouter le titre à cette catégorie.

05:05 C'était donc nouvelles annonces. Ici, on va mettre ce titre un peu plus grand comme ceci. Et donc maintenant, ce qu'on va faire, c'est qu'on va créer quand même un nouveau fichier côté front et côté serveur ici, on va l'appeler par exemple others point serveur point t s. Donc dedans, on va exporter une fonction qu'on va appeler get others. Cette fonction sera donc asynchrone et elle va renvoyer un tableau d'offres comme ceci.

05:28 Donc on va demander à Copilot de nous rédiger quelques offres. On peut tout de suite définir le type. Pour le moment, on va définir un type, mais ensuite, ça sera Prisma qui définira le type de lui-même et on va renommer les propriétés. On va dire que Price, ça s'appelle Price r power et ça sera donc un number. La description et le lieu, c'est toujours un string et la date publish, ça sera un date comme ceci.

05:50 Maintenant, notre méthode qui va pour le moment pas être asynchrone parce que ce sera collé en va renvoyer un tableau d'offres. Donc on va demander à Copilot please, Generate, pend, offers, following, this. Et là Copilot vient de me générer du coup les dix offres que je lui ai demandé. Malheureusement, le titre n'est pas très original. On boucle juste dix fois sur la même offre à cet endroit-là, mais le prix au moins est aléatoire et on utilise bien une date de publication.

06:15 Pour l'instant, ça va nous convenir. On va rester très simple, on va petit à petit ajouter la logique à cette application. Du coup, maintenant, on est dans notre index ici, donc dans notre page index point TFX. Et comme tout autre route de remix, on peut exporter une fonction qu'on va appeler l'odeur et une fonction asynchrone et une fonction au final de remix qui nous permet de faire du rendu côté serveur, donc du SSR. Et à cet endroit-là, on va appeler notre méthode getoffers qui n'est donc pas asynchrone et qui nous renvoie un tableau Dooffers.

06:44 Et on peut renvoyer le tableau Dooffers comme une propriété de cet objet qu'on renvoie depuis le serveur. Et enfin, dans notre index, ici, on peut récupérer nos données en utilisant le hook use loader data. On importe depuis un x run, slash, react. Et on ne va pas oublier d'insérer le type générique et on peut voir que, en données, on récupère un objet qui contient une clé offers. Donc, nous allons déstructurer cet objet pour récupérer nos offres.

07:09 Et maintenant, on peut boucler sur nos offres ici. Donc, on va boucler sur chacune de nos offres et pour chacune de nos offres, nous allons return notre service gardien. Et bien sûr, il nous manque la propriété qui, donc on va peut-être demander à Copilot de nous la rajouter. On aimerait une ID sous forme de string et c'est, et on va laisser du coup Copilot nous générer l'ID sous forme de string comme ceci. Maintenant, on a notre clé et c'est tout simplement l'ID de notre offre.

07:34 Donc, ce qui est cool, c'est que ça boucle dix fois sur notre offre qui a été codée en dur. Maintenant, ce qu'on va faire, c'est qu'on va supprimer ses propriétés et on va à la place rajouter un argument à notre composant et cet argument va récupérer les props que nous avons définis ici dans ce type. Donc, nous allons insérer les props de la méthode getofers. Pour ce faire, on va utiliser le type awated return type, type of get offers, donc notre méthode getoffers. Là, on récupère les types de retour après l'avoir awaited.

08:04 Effectivement, je viens d'oublier, mais on n'a pas besoin de la witch vu que cette méthode n'est pas encore asynchrone. Elle le sera plus tard avec la base de données. Donc, on peut retirer le awaited et on peut ensuite dire comme c'est un arrêt d'offre qu'on veut récupérer le type d'une des offres en rajoutant ici les crochets et un zéro. Et maintenant, on peut voir qu'on peut récupérer chaque propriété de notre offre dans v s code. Mais on va faire quelque chose d'un peu plus simple parce qu'on n'a pas envie ici de spécifier chaque propriété de notre objet.

08:30 On va à la place dire que le type va contenir une propriété offre et à cet endroit, on déstructure notre offre. Et ensuite, ici, on a juste à passer notre offre directement comme ceci. Donc maintenant que c'est fait, on peut supprimer le titre qui avait été collé en dur et nous allons déstructurer chaque propriété de notre offre. On a donc une date, la description, le lieu, le prix par heure et le titre. Pour le prix par heure, pour le moment, on va mettre to fix deux et on va rajouter euro lâche h comme ceci.

08:58 Et pour la date, on va mettre par exemple to local date string FRFR et on va tenter le dead style short. Bon, on peut voir qu'on a un petit problème ici parce qu'en fait ici date publishd est considérée comme une date, mais comme nous l'avons renvoyé sous forme de Jason, elle a été parfait en tant que string. Pour refléter ce changement qui n'a pas été détecté par le framework, on peut rajouter un seria lives from qui est un type qu'on peut importer depuis remix run slash mode ici, on va l'importer en tant que type et on va englober notre type de fonction ici, comme ceci. Et maintenant, on peut laisser notre date sous forme de string à cet endroit-là. On peut voir qu'elle n'a pas été éparchée, on va le faire tout à l'heure, mais maintenant, on retrouve bien toutes nos offres ici avec leurs prix respectifs et l'erreur de type a disparu.

09:44 On peut maintenant supprimer les données qu'on avait codé en dur. On les supprime également côté serveur parce qu'elles ne vont plus nous servir. Maintenant que c'est fait, on va faire autre chose. Ces offres-là, pour le moment, on boucle dessus quand on appelle la méthode getoffers. Ça veut dire qu'à chaque fois qu'on appelle la méthode getoffers, ça va recréer tout simplement nos dix offres.

10:02 Et c'est donc ce qu'on pourrait faire, c'est qu'à la place, on va sortir cette logique de la fonction et seulement renvoyer notre tableau d'offerts pour le moment, comme ceci. Ce qui veut dire que ça va toujours renvoyer les mêmes offres à la même date ici. On peut voir que la date ne change pas tant que le serveur est actif. Pourquoi on fait ça Parce qu'on va tout de suite ajouter la logique et la page de l'offre. Pour ce faire, on va dire que ce Dibla qui englobe donc toute notre offre, ça va être à la place un Link comme ceci.

10:30 On va donc importer le Link depuis Remix et on va rediriger vers la page qu'on va appeler par exemple others slash other point eddy. Comme ça, quand l'utilisateur clique sur son annonce, ça le redirige sur la bonne page. On va également rajouter un petit effet au over et on va mettre que la bordure va se changer, par exemple en vert plutôt. Et voilà, maintenant, on a donc nos offres. On passe la souris dessus, on peut voir que ça met la bordure en vert.

10:56 Et si on clique sur l'offre, on peut voir qu'on est redirigé sur la page others slash trois. Ça veut dire qu'on va récupérer l'offre numéro trois de la page others. Donc ce qu'on peut faire maintenant, c'est qu'on va tout de suite créer cette nouvelle page. On retourne donc dans nos dossiers. Ici, dans le dossier route, nous allons créer un nouveau fichier qu'on va appeler offers et on va mettre un point pour symboliser le slash dans l'url et on va donc mettre offers ID point TFX.

11:21 Et comme ça, ça va créer une route à payer qui va nous renvoyer les informations de notre offre. Ce qu'on peut faire maintenant, c'est qu'on va rajouter une méthode l'odeur de type asynchrone qu'on va exporter comme ceci. Et ensuite, nous allons renvoyer du json et nous allons rajouter un petit morceau de logique dans cette méthode. Déjà, nous allons récupérer l'argument params ici qui va nous permettre de récupérer le paramètre du Harl. On peut voir sur Chrome que le paramètre, c'est l'offre numéro trois, donc le paramètre est égal à trois.

11:49 Ici, je vais donc récupérer le paramètre offert ID que j'ai appelé offert ID en nommant le fichier ici $offer ID, ce qui symbolise le nom du paramètre pour Linux. Et donc, ce paramètre, on peut le récupérer dans l'objet params qui contient donc une propriété offert ID qui aura comme valeur croix. Pour être sûr de ça, on peut faire un console log de notre fichier par comme ceci. Maintenant, si on actualise la page, on peut voir dans la console, on a bien un offert ID qui est égal à trois. Si maintenant remplace le console log et on console log notre offert ID ici et qu'on rafraîchit la page, on peut voir qu'on a bien le chiffre trois dans notre terminal.

12:28 C'est un chiffre, mais il faut savoir que tous les paramètres d'URL sont considérés comme des strings. Pour le moment, quand on va ajouter notre logique à la base de données, l'ID ne sera plus un number. On va utiliser des qu ID. Maintenant, ce qu'on a envie de faire, c'est de récupérer nos offres. On l'avait défini dans le fichier others point serveur ici.

12:45 Donc, on a bien des offres ici qu'on peut même exporter directement et on va ensuite pouvoir les importer dans ce fichier là. Mais d'abord, on va rajouter un petit morceau de logique parce que ici, on peut voir que Lofor ID, si on passe la souris dessus, c'est soit un string, soit un define. En vrai, c'est impossible que cette route soit chargée si jamais le paramètre d'URL est undefined, mais nous allons quand même devoir ajouter un petit morceau de logique pour throw une erreur si jamais c'est undefined. Et on peut tout simplement mettre un if not of for aded alors throw new erreur did not find offert ID comme ça. À partir de cet endroit là, si ça ne se trouve pas, ça veut dire que l'offer ID, il est défini.

13:24 Donc maintenant, ce qu'on va faire, c'est qu'on va importer notre tableau d'offeres depuis le fichier serveur offert. Serveur et nous allons essayer de chercher une offre qui contient notre ID. On va donc l'appeler found offert est égal à notre tableau d'offres et on va simplement faire un find sur l'ID comme ceci. Bien sûr, si on ne trouve pas l'ID, on va devoir de nouveau trouver une erreur. Dead not find offert a l'air d'être un bon message d'erreur.

13:49 Sinon, si on trouve notre offre, alors on va la renvoyer sous forme de Jason, notre application. On va donc la renvoyer et nous allons l'appeler par exemple offert qui sera égal à bound offert. Maintenant, on va actualiser la page comme ça et on va voir que ça n'a pas trop d'erreur parce qu'on a trouvé une offre qui a l'ID de trois et le prix est à douze euros de l'heure. Si on actualise la page, on peut voir que ça continue d'être la même offre au même prix et au même endroit. C'est bien, mais maintenant essayons d'utiliser une offre qui n'existe pas.

14:16 Par exemple, l'offre numéro trente-et-un, ici, on peut voir que ça a renvoyé une erreur depuis le serveur, Dead not find offert. Et ça a renvoyé cette erreur parce qu'on n'a pas retrouvé cette offre. Elle n'existe pas encore sur notre plateforme. Là, on a commencé par la logique côté serveur. Mais ce qu'on va faire maintenant, c'est qu'on va ajouter un composant côté client.

14:36 Donc, on va exporter une fonction par défaut qu'on va appeler par exemple offert page qui va contenir notre layout ainsi que notre offre. Je vais laisser Copilot me suggérer la suggestion parce que c'est exactement ça. On utilise comme dans l'index le o q uz loader data pour récupérer la donnée qui nous est renvoyée depuis le loader ici. On récupère donc une offre à cet endroit-là et on va à présent afficher son titre. Maintenant, si on sauvegarde la page, on peut voir qu'on a bien l'offre numéro quatre sur l'ID trois ici.

15:05 On va essayer avec l'ID un, on a l'offre numéro deux, avec l'ID zéro, on a l'offre numéro un et avec l'ID trente-et-un, on a toujours cette erreur, sauf que cette fois, elle s'affiche côté client. Ce qu'on peut faire du coup pour éviter d'afficher un message d'erreur aussi brutal, c'est exporter un composant qu'on va appeler error boundery comme ça. Et donc pareil, ça va être donc une fonction qui va renvoyer une erreur et on va plus tard récupérer le message d'erreur personnalisé de cette erreur. Mais pour le moment, ça nous va très bien. On préfère avoir ce genre de message d'erreur si jamais on ne trouve pas d'annonce.

15:38 Maintenant, ce qu'on peut faire, c'est qu'on peut quand même dans notre message d'erreur, récupérer les informations de l'offre, c'est-à-dire son paramètre ici, grâce au hook qui s'appelle use Params ici. Et on va donc le déstructurer pour récupérer le offert edit. Et donc, à la place de mettre annonce introuvable, il semblerait que l'annonce, on va mettre un span en bold et on va afficher le paramètre trente-et-un. Vous cherchez, il n'existe pas. Comme ça, si jamais quelqu'un cherche annonce, par exemple, hello, c'est moi, ça va afficher le paramètre que l'utilisateur a mis dans l'URL qui n'a pas été trouvée par l'application.

16:11 On peut faire aussi maintenant, c'est rajouter un lien vers la home page qui va dire retourner à la liste des annonces. On va maintenant appeler notre bouton varians, qui est une méthode qui va nous permettre d'utiliser les styles qu'on a définis pour nos boutons et on va effectivement utiliser le variant primary comme ceci. Maintenant, le lien change. En fait, c'est sur la page d'accueil qu'on retrouve nos offres. Donc, on a donc Annonce introuvable.

16:32 Il semblerait que l'annonce Allô, c'est moi que vous cherchez n'existe pas. Retournez à la liste des annonces. Si on clique dessus, on retourne à la liste des annonces avec chacune de nos annonces. Maintenant, on peut cliquer sur l'annonce numéro cinq ici, et on retrouve de nouveau le titre et le style de notre offre numéro cinq parce que cette offre a été trouvée par l'application. Ok, donc maintenant, on va afficher les informations de notre offre comme ceci, on va afficher le même prix, la date et le lieu.

16:58 J'ai demandé à un pilote de me formater le prix en euros ici et la date dans le même style. Ce qu'on peut faire maintenant, c'est qu'on va créer des méthodes réutilisables pour les formater. Ici, dans le fichier app, à cet endroit-là, on va dans la librairie Utiles qui a été créée automatiquement par Chelsea NUY, nous allons exporter une fonction qu'on va appeler formate date et qui va prendre en paramètre une date de type date et nous allons renvoyer la date formatée en renvoyant donc le code que Copilot nous a écrit et on va faire exactement la même chose pour le prix avec une méthode qu'on va appeler format de presse qui va prendre en paramètre un prix, sera donc de type number et nous allons à présent copier cette logique-là et nous allons la coller comme ceci. Cette fois, qu'on va formater, c'est le prix comme ceci. Et maintenant, à la place d'utiliser ce code un peu illisible, on va appeler notre méthode formate price pour le prix, qui s'attend à recevoir un objet et également la méthode formate date pour la date qui s'attend à recevoir un objet.

17:55 On peut voir qu'on a malheureusement une petite erreur à cet endroit-là parce qu'elle s'attend à recevoir une date, mais elle a reçu en fait un string. On va modifier ce type pour dire qu'on accepte les dates et les strings. Pour l'instant, ça ne va poser aucun problème. Maintenant, si on retourne dans le fichier index point TFX, on peut utiliser de nouveau notre méthode formate Price qu'on réutilise comme ceci et pour la date, on va faire exactement la même chose. On va appeler formate date comme ceci.

18:21 Si on retourne maintenant sur la page d'accueil, on peut voir que ça a toujours très bien formaté notre donnée. Ici, on peut retirer le euro parce qu'il a été automatiquement ajouté avec JavaScript. Maintenant qu'on a fait ce reformatim, on va créer des offres pour de vrai dans notre base de données. Pour ce faire, on va ouvrir le fichier qu'on avait appelé offert point serveur point t s et nous allons nous inspirer de ce type pour créer notre modèle Prisma. On copie donc ce type et on va ouvrir maintenant le fichier schéma.

18:49 Prima ici. Et ce que nous voulons faire, c'est que nous voulons transformer ce type en modèle. Donc, on va le renommer en modèle et on va transformer chacun des champs en modèle Prima compatible. L'ID sera donc comme nos autres ID, ce sera un coID par défaut. Nous allons pouvoir copier ce champ et le remplacer celui-là.

19:08 Pour le titre, ce sera donc un string. La description sera également un string. Nous aurons deux dates créées par défaut et gérées par Prima. Ce sera donc les dates createdatedated at. Et nous allons également rajouter un champ qu'on va appeler au final prix, parce que ce n'est pas sûr qu'on va avoir comme modèle un prix par heure.

19:26 Il y aura peut-être des situations où on facturera la prestation en une fois, où on ira par exemple jusqu'à vendre notre équipement. Donc, le prix n'est pas forcément par heure. On va retravailler ensemble le modèle économique de cette application. Ce qu'on a envie, c'est de rajouter quelques champs supplémentaires. Par exemple, nous allons rajouter un champ qu'on va appeler active, qui sera un booléan et ça sera le statut de l'offre par défaut.

19:49 Est-ce que l'offre est visible sur le site Ce champ pourra être modifié par l'utilisateur de l'offre s'il veut, par exemple, suspendre la visibilité de son offre. Ensuite, nous aurons un autre champ qu'on va appeler re curing qui est également un booléen. Et ce champ signifie que le service peut être répété plusieurs fois ou pas. Exemple si l'utilisateur vend un objet, il ne peut vendre cet objet qu'une seule fois. Par contre, s'il rend un service, il peut le rendre plusieurs fois.

20:18 Dans ce cas, l'application ne va pas supprimer l'offre après la première vente. C'est à peu près ça l'esprit de récurrence dans notre application. Par défaut, ça ne sera pas récurrent et par défaut, les offres ne seront pas d'active. Donc on va leur assigner la valeur de false par défaut. Ensuite, nous allons rajouter un champ user ID et le user ID sera tout simplement le propriétaire de cette offre, donc l'utilisateur qui a créé cette offre.

20:43 On va donc copier exactement cette syntaxe et nous allons rajouter un champ au niveau des utilisateurs qu'on va appeler offers. Ça sera donc une relation parce qu'un utilisateur peut avoir zéro, un ou plusieurs offres. Par contre, une offre ne peut avoir qu'un utilisateur. Ce qu'on va faire aussi, c'est que nous allons créer tout de suite un nouveau modèle qu'on va appeler transaction et ce sera le modèle que l'utilisateur qui souhaite faire une offre va créer. Ce modèle prendra plusieurs paramètres.

21:09 Il prendra donc une ID, une date de création et de mise à jour et ça sera aussi une relation vers l'offre. Donc, on va modifier ce champ et on va appeler ça offert ID. Ce sera donc une relation vers notre objet offert qu'on va donc appeler offert ID comme ceci et au même titre que pour les utilisateurs, une offre pourra être liée à plusieurs transactions. La relation sera donc 1 to many comme ceci. Par contre, il y aura également à notre transaction un utilisateur ici, mais ce ne sera pas le propriétaire de l'offre, ça sera la personne qui souhaite effectuer une transaction.

21:42 De cette manière, on récupère les informations du propriétaire de l'offre avec son champ user ID et on récupère les informations de la personne qui souhaite faire une offre dans le champ user ID de notre objet transaction. Il nous manque donc une dernière chose à faire dans notre modèle user, nous allons rajouter un champ transaction comme ceci. On a fini de modifier notre schéma Prima, donc on va ouvrir le terminal et nous allons arrêter de faire tourner le serveur et nous allons nous déplacer dans notre fichier backend. On va en faisant un cd backend parce que c'est dans le fichier backend que se trouve notre fichier prisma. Et nous allons lancer la commande suivante NPX prisma migrete dev tiret tiret name add others and transactions comme ceci.

22:24 Maintenant, on peut appuyer sur Entrée pour lancer cette commande et une nouvelle transaction vient d'être créée pour notre base de données. C'est super. Maintenant, on peut faire un cd double point pour retourner en arrière et on peut lancer la commande NPM rundev à nouveau. Maintenant, ce que nous allons faire, c'est que nous allons retourner dans le fichier others. Serveur et au lieu de boucler sur des offres sans coder en dur, nous allons à la place les charger depuis notre base de données.

22:49 Donc nous allons supprimer toutes ces lignes et nous allons à la place utiliser notre service Prisma. Pour ce faire, comme on est dans notre application remix, on a besoin de passer par le contexte. Donc la méthode getofers va maintenant prendre en paramètre un contexte. Et ce contexte, nous allons le typer par app load contexte. On importe depuis remix run slash noad et c'est dans ce contexte qu'on va retrouver notre service remix service.

23:16 On peut donc comme ceci accéder à Prima et nous pouvons maintenant faire un offert point find many. Maintenant, nous voulons choisir les champs qui vont être renvoyés côté client. Donc, nous allons faire un select et nous allons sélectionner les propriétés suivantes. On va prendre l'ID, le titre, la description, le prix et la date de mise à jour de cette offre. Ce Ce qu'on veut faire aussi, c'est rajouter une condition dans un where.

23:38 Nous voulons seulement récupérer les offres qui sont actives. Maintenant, si nous retournons dans le fichier index point TFX, on peut voir ici que les types ont changé, les types qui sont retournés depuis la méthode getofferes, parce que maintenant, déjà le premier changement, c'est que c'est une promesse. Donc nous allons devoir la white. Comme c'est une promesse, ici nous allons devoir ajouter un mot clé asynchrone, pour marquer cette méthode comme étant asynchrone et la méthode fine many est une promesse. Nous allons donc devoir faire un await comme ceci et comme nous avons fait un await, nous devons remplacer le type avant le sérialize from, nous allons devoir rajouter un awaited pour englober notre type de retour de fonction comme ceci.

24:18 Maintenant, certains des chambres ont été renommés. Par exemple, le prix ne s'appelle plus price perhower, il s'appelle price. Donc, nous allons modifier le prix comme ceci. La variable place, donc le lieu de la transaction n'a pas encore été ajouté. L'image non plus d'ailleurs et la date de publication s'appelle en fait updated at comme ceci.

24:37 Maintenant, on doit faire un dernier petit changement. Ici, dans notre méthode lawder, on doit déjà la méthode getoffers, mais en plus, maintenant, elle prend un argument, elle prend notre contexte. Et ce contexte, on le récupère depuis notre loader parce que c'est un des arguments que Remix nous met à disposition. On va donc ouvrir les crochets et on va typer ces arguments par loader ponction argues comme ceci, on peut importer ça en tant que type et Remix nous met donc à disposition trois arguments, y compris notre contexte. Il nous suffit donc de récupérer le contexte comme argument et de le renvoyer à notre méthode getofers comme ceci.

25:13 Et maintenant, on peut voir qu'on a réglé toutes les erreurs pour ce fichier. L'application a crash, donc on va devoir relancer le terminal avec un NPM rundev. Maintenant, on va actualiser la page et on va se rendre compte qu'il n'y a aucune annonce dans notre base de données. Ce qu'on va faire du coup, c'est qu'on va ouvrir un terminal et on va lancer la commande NPX prizma studio, mais nous allons la lancer dans le dossier backend. On va donc dans backend et on va faire un NPX prizma studio, ce qui nous ouvre un deuxième onglet avec nos offres ici et nous allons ajouter à la main une offre.

25:45 Ça sera donc tondeuse à gazon. On ne va pas toucher au champ par défaut. La description sera, je viens chez vous avec ma tondeuse et je tends votre gazon en trente minutes Et le prix sera de trente euros. Bien sûr, ce que nous souhaitons, c'est que l'offre soit active et la relation avec l'utilisateur sera le premier utilisateur virgile arobase algo max point f r. On peut donc enregistrer cette offre et nous allons maintenant sur notre application pour voir si l'offre est active, on actualise la page et on ne la trouve pas.

26:14 C'est tout à fait normal, c'est parce qu'on a demandé à Prizman de nous afficher seulement les offres qui étaient actives. Et c'est, si on s'amuse à commenter cette condition et qu'on rafraîchit la page, on peut voir qu'on retrouve notre offre. Sauf que ce n'est pas ce qu'on veut. Nous voulons seulement afficher les offres qui sont disponibles, donc qui sont actives. Donc nous allons décommenter cette ligne et nous allons à la place dans Prima Studio modifier notre offre pour la définir comme étant active.

26:40 On va donc mettre le champ active à flow. On peut également la mettre comme récurrente parce que ce service peut se faire plusieurs fois, comme ceci. Maintenant qu'on a ajouté ce champ, on peut kill le terminal de Prisma Studio et nous pouvons supprimer cet onglet. Si on rafraîchit la page, on retrouve donc notre première offre qui s'appelle tondeuse magasin. Et si on clique dessus, on peut voir qu'on a une petite erreur, annonce introuvable.

27:03 Semblerait que l'annonce ID que vous cherchez n'existe pas. Effectivement, nous n'avons pas fini notre refactorisation. On va donc maintenant modifier le fichier qu'on a appelé offert point dollar offert ID point TFX. Ici, à l'époque, on récupérait nos offres qui étaient collées en dur. Sauf que notre tableau d'offres n'existe plus.

27:20 On ne va donc plus faire comme ça. On va à la place retourner dans le fichier offert point serveur et nous allons créer une nouvelle méthode qui va s'appeler get offert au singulier qui va prendre donc en paramètre une offert ID sous forme de string. Ça sera donc un offert ID sous forme de string comme ceci. Et à la place de faire un find many, nous allons faire un find unic en ajoutant le champ where sur l'ID, l'ID qui sera donc égal à offert ID. Maintenant, dans notre fichier offert point dollar offert ID, nous allons, au lieu de faire un find sur toutes les offres, appeler la méthode await get offer.

27:55 On a donc deux paramètres, une offert ID et notre contexte. Bien sûr, le contexte n'existe pas encore, car nous ne l'avons pas déstructuré. On va donc récupérer en paramètre de notre loader, le contexte de remix. On peut voir qu'après avoir utilisé la page, notre page s'affiche tout de suite. On a donc les informations sauvegardées en base de données, notre tondeuse à gazon, la description, la date n'existe pas et le prix n'existe pas.

28:18 On a renommé les champs dans la base de données. Donc on a juste à faire une petite refactorisation, le champ s'appelle Price et la date s'appelle updated at. Il n'y a même plus le lieu de la transaction. On peut voir maintenant que ça a bien pris en compte les changements. Nous sommes le douze mai deux-mille-vingt-quatre, qui est donc la date à laquelle cette annonce a été mise à jour et le prix est de trente euros.

28:43 Par contre, on peut retirer par heure, car on a supprimé cette notion de prix par heure. Maintenant, ce qu'on va faire, c'est créer une interface pour pouvoir modifier et créer des nouvelles offres sans passer par Prima Studio. On va donc se connecter à notre utilisateur, Virgile atalcomax point f r et nous allons ajouter une page ici qui correspond à cette icône, l'icône de euro et nous allons fermer tous nos onglets dans v s cred, on va ouvrir l'explorateur de fichiers dans le fichier root ici et nous allons créer une nouvelle page que nous allons appeler my-services point TFX. Pour ce faire, nous allons copier la structure de la page d'accueil comme ceci, donc ce composant exporté par défaut et nous allons le coller ici. Nous allons renommer le composant en my services, on va donc appeler ici mes services.

29:31 Maintenant qu'on a créé ce composant, on doit retourner dans notre navbar ici et notre icône qui s'appelait recepte euro va nous rediriger vers la page my services comme ceci. Cette route sera bien sûr protégée, c'est-à-dire que nous allons donc charger côté serveur les offres de l'utilisateur connecté. Donc on va commencer par ajouter une méthode lewder, on importe par défaut. Ensuite, nous allons appeler la méthode await require user et cette méthode prend en paramètre le contexte de l'application qu'on récupère encore une fois dans la méthode Loader. La méthode require user force l'authentification de notre utilisateur pour cette page.

30:09 C'est donc une route protégée qui ne peut être accéder uniquement par un utilisateur authentifié. Si on regarde le code, ici en cliquant sur la méthode, on peut voir qu'on récupère donc un utilisateur optionnel dans la session, mais si l'utilisateur n'existe pas, alors on fait un d'une redirection vers la page login. Ce qui signifie qu'en cas de non authentification de notre utilisateur, on le renvoie sur une autre page. Il ne pourra donc pas accéder à cette page. Cette vérification étant faite, nous sommes donc maintenant protégés sur la page my tiret services.

30:40 Maintenant que c'est fait, nous allons créer côté serveur un autre fichier dans le dossier qu'on a appelé serveur et ça sera profile point serveur point t s. Et là-dedans, nous pouvons faire exactement comme dans le fichier others point serveur ici. Nous allons donc copier cette méthode et récupère toutes les offres dans la base de données et nous allons la coller ici, sauf que nous allons l'appeler get user others à la place. On va donc réimporter notre type ici et que nous voulons faire récupérer les offres de l'utilisateur qui est connecté Pour ce faire, nous avons besoin d'un deuxième argument qui s'appelle user ID, qui va nous permettre d'identifier les offres de l'utilisateur. Une fois cet argument rajouté, on peut donc récupérer la variable user ID ici et nous allons maintenant rajouter une condition dans notre fine many qui n'est donc plus le where active est égal à trop parce que l'utilisateur pourra voir toutes ces offres, même celles qui sont invisibles.

31:34 Par contre, nous voulons récupérer seulement les offres qui sont égales à user ID. Comme la variable s'appelle user ID, nous n'avons pas besoin de lui assigner sa propre valeur vu qu'elle porte le même nom. Maintenant, nous pouvons appeler cette méthode getuserothers dans notre route MyService en faisant un await de get userothers. Sauf qu'on a un petit problème. On a besoin de faire passer le contexte et on a besoin de faire passer le user ID.

32:00 Alors, comment est-ce qu'on va récupérer cette information côté serveur dans le type de retour que nous renvoie require user ici. On peut donc récupérer les informations que nous renvoie cette méthode dans une variable qu'on va appeler user et on peut voir que notre user contient un email, un name et une ID. User ID est donc égal à user point ID. Maintenant, nous allons récupérer les informations, donc notre tableau d'offres pour cet utilisateur dans une variable qu'on va appeler offers et nous allons la renvoyer côté client comme ceci. Mais je vais préférer la syntaxe json, on importe depuis remixran slash nod qui va renvoyer un objet qui s'appellera user.

32:37 Maintenant que c'est fait, on peut s'inspirer du code qu'on avait en page d'accueil dans le fichier index point TFX. Ici, on peut récupérer ce que nous renvoie le serveur, donc en utilisant le hook useloader data, on va copier cette syntaxe tout de suite comme ceci. On a juste maintenant à importer le hook useloader data et nous pouvons également boucler sur nos offres, donc les offres qui sont disponibles pour cet utilisateur. On va donc copier cette boucle comme ceci et nous allons même importer le composant service card qui sera exactement le même. On importe donc service card et on importe le même type ici.

33:12 Sauf qu'au lieu d'hériter du type getofers, il va hériter du type get userofers parce qu'on peut charger des informations supplémentaires. On importe donc le lien sera redirigé. Ce sera redirigé vers la page my service au for ead, car nous voulons accéder à l'interface Creud de l'utilisateur et nous pouvons maintenant réimporter toutes les fonctions que nous avons utilisées dans ce composant. Sauf que nous voulons une information supplémentaire. Donc, on va retourner dans le profil ici et nous voulons également sélectionner le statut active et le statut récuring ici.

33:46 De cette manière, on sera capable d'afficher une petite pastille sur notre annonce pour savoir lesquelles sont actives, lesquelles sont inactives. Maintenant, si on retourne dans le fichier my tiret services point TFX, ici, ce qu'on a envie de faire maintenant, c'est d'accéder à notre page. On va cliquer sur l'icône de euros ici pour accéder à la route qui s'appelle my Services. La première chose qu'on veut faire maintenant, c'est de s'assurer que cette offre nous appartienne bien. Pour ce faire, on va se déconnecter et on va essayer de réaccéder à la route qui s'appelle My Services.

34:16 On peut voir qu'on est tout de suite redirigé sur la page login. Ça signifie que la route a bien été protégée par notre fonction require user. Maintenant, on va donc se connecter à un autre utilisateur, par exemple, virgile trois arobase recomax point f r. En se connectant, on est donc redirigé sur la page d'accueil et si on essaie d'accéder à la page My Services, on ne retrouve plus l'offre qui est active. Et c'est bien parce que cette offre ne nous appartient pas, elle appartenait à l'utilisateur qui s'était connecté avant.

34:44 On peut donc de nouveau se déconnecter, car on a vu ce qu'on voulait voir, la page est bien protégée et l'offre n'apparaît pas pour tous les utilisateurs. Elle n'apparaît seulement pour son propriétaire. On va donc se reconnecter avec l'utilisateur. Il possède cette offre et on retourne sur la page my tiret services. Maintenant, on va ajouter une petite condition pour voir si cette annonce est bien active.

35:04 Nous allons donc récupérer la propriété active ici et si l'annonce est active, alors nous allons afficher un spam avec un message cette offre est active. Sinon, nous allons afficher un spam avec le message cette offre est inactive. Nous allons donc rajouter une petite couleur en cas d'inactivité comme ceci. Si l'offre est active, l'utilisateur saura qu'elle est donc visible et disponible et si elle ne l'est pas, il le saura avec ce petit message qui sert de feedback à cet utilisateur. Maintenant, on a envie de créer un formulaire pour pouvoir créer des nouvelles offres et modifier des nouvelles offres.

35:37 Pour ce faire, nous allons d'abord créer un bouton qu'on va ajouter tout en haut. On va donc ajouter le composant link ici, parce qu'on va rediriger l'utilisateur sur la route, qu'on va bien appeler myservices new et nous allons l'appeler ajouter une annonce. Nous allons à la place utiliser l'un des styles que nous avions créés dans nos boutons variants. On va utiliser par exemple le variant primari et nous allons ajouter la classe wheels fit pour que le bouton ne prenne pas toute la largeur de la page. En cliquant dessus, ça va nous rediriger sur la page my-services slash new, qui est une page que nous n'avons pas encore créée.

36:12 Nous allons donc tout de suite la créer et ce que nous avons envie de faire, c'est de nous inspirer du formulaire qui s'appelle Register ici. Cette page Register parce qu'elle contient déjà un formulaire qui est prêt à être réutilisé ainsi qu'une action. Donc nous allons copier cette logique de la route register et nous allons créer une nouvelle route que nous allons appeler my tiret services point dollar offert id point TFX. Et c'est dans ce fichier que nous allons coller toute cette logique de création d'offre. Pour le premier service, nous allons copier exactement le même loader que dans la route my tiret services comme ceci.

36:50 Nous allons modifier ce l'odeur et nous allons importer celui-là avec la méthode require user. Par contre, à la seule différence que nous n'allons pas charger la liste des offres de l'utilisateur. Nous voulons seulement nous assurer que l'utilisateur est bien identifié pour créer une offre. Comme nous n'avons pas besoin des informations de l'utilisateur côté client, on peut renvoyer nul et nous pouvons arrêter de sauvegarder ces informations dans une variable, ce qui rendra le code un peu plus lisible. Maintenant, nous allons modifier ce schéma qu'on va appeler create offert skima et on peut voir si on ouvre notre page Prima sur le côté qui définit donc notre modèle de données pour les offres qu'on a besoin de toutes ces informations.

37:28 On va donc les copier ici pour être sûr de ne pas les oublier et nous allons donc rajouter une propriété title, notre code qui sera une propriété obligatoire. Votre annonce doit avoir un titre et nous allons rajouter une propriété description qui est également obligatoire. Votre annonce doit avoir une description. On peut supprimer la propriété mot de passe et nous n'allons pas rajouter le champ active tout de suite parce qu'au moment de créer son annonce, l'utilisateur ne pourra pas tout de suite l'activer. Il devra d'abord rajouter par exemple des images à son annonce.

38:01 Ce qu'il peut rajouter par contre, c'est le prix qui sera un number avec comme message d'erreur votre annonce doit avoir un prix. On peut rajouter une pipe ici pour dire que le prix minimum d'une annonce est de zéro et on peut rajouter un deuxième pipe pour dire que le prix maximum est de cinq cents euros parce qu'on a quand même envie de prôner la générosité sur notre application. Ça va suffire pour le moment. Nous allons donc conserver ce schéma et nous allons commenter notre action. On va y revenir un peu plus tard.

38:30 Et ce que nous voulons faire, c'est donc utiliser ce schéma avec conforme et avec donc le hook use form. Pour le moment, on a une petite erreur avec le type générique de notre action. Donc ce qu'on va faire, c'est que nous allons décommenter l'action, mais qu'elle va renvoyer nul, tout simplement. On va donc renvoyer du JSON avec un résultat qui sera égal à nul comme ceci. Maintenant, retournons dans notre composant ici.

38:54 Ce qu'on a envie de faire, c'est de pouvoir voir cette vue. On va donc actualiser la page comme ceci et on peut voir qu'on retrouve la page principale qui s'appelle My tiret services. Et c'est parce que nous avons créé ce qu'on appelle une route imbriquée. Mais ce n'est pas ce qu'on veut. On n'a pas envie d'afficher le parent, donc on va renommer notre fichier my tiret services et nous allons l'appeler my tiret services point index.

39:18 Simplement pour informer Remix que c'est l'index et c'est le détail d'une offre. Si maintenant on rafraîchit la page, on retrouve le formulaire qui est présent dans notre composant et nous allons maintenant le modifier en live. On retourne donc dans le fichier myservices point dollar offert ID et nous allons renommer ce composant en create offert et nous allons modifier le titre, ajouter une offre. Ça sera donc un formulaire de type post ici et nous allons récupérer les propriétés de chacun des inputs. Le premier sera donc titre de l'offre et on va utiliser la propriété qu'on a appelée title, comme ceci.

39:53 La deuxième propriété sera donc la description de l'offre et on va utiliser la propriété description. On n'oublie pas bien sûr de changer le type de l'input qui sera donc plus un email, mais un texte et le dernier champ, ça sera le Price. Le Price, ça sera donc un input de type number et on va le nommer prix de l'offre comme celle-ci. On peut maintenant renommer ce bouton et l'appeler créer cette offre. Ce qu'on a envie de faire cependant, c'est quand même d'utiliser un composant un peu plus stylé, un textariat pour notre description parce que ce champ-là, il est un petit peu trop court.

40:26 Ce composant Field que nous avons créé ensemble est un composant qui nous permet d'afficher un message d'erreur, un label et un input chaque CNUY. Et ce composant, je l'avais retrouvé sur le repository GitHub de Ken Synode qui s'appelle l'epic stack. On peut donc retourner sur l'epic stack ici et nous allons aller dans le dossier app, dans le dossier composant et dans le dossier forms ici et nous retrouvons cet input donc ces composants qui s'appelle field et ce que nous voulons maintenant c'est copier le deuxième composant ici qui s'appelle textteria field parce qu'on en a besoin pour afficher un textariat et nous allons même aller jusqu'à copier cette input là qui s'appelle aussi checkbox parce qu'on en aura également besoin. Donc on copie ces deux composants et nous allons les coller dans notre fichier Farm ici. Mais on peut voir quand je les copie, il fallait les déplier parce que là j'ai copié seulement les deux lignes.

41:19 Donc je vais tout copier comme ceci et tout coller comme ceci. Maintenant on va changer le type des erreurs, c'est string ou undefine, comme ceci, pour nos deux inputs. Pour l'input de type texteria, nous avons besoin d'utiliser le hook use ID et les composants label et texteria depuis chaque CNUY. Sauf que pour le moment, on ne les a pas encore importés. On avait seulement importé le bouton et l'input.

41:45 Donc, on va devoir retourner sur le site de Shell CNUY pour importer tous les inputs dont on a besoin. On a donc besoin d'importer le composant label ici. Donc, nous allons ajouter ce script dans notre terminal. On peut même qu'il le projet et lancer cette commande, sauf que nous voulons aussi ajouter un deuxième composant qui s'appelle texte arrière ici. Nous allons rajouter le nom de ce composant dans le terminal et nous voulons aussi ajouter un composant qui s'appelle checkbox ici.

42:11 Donc, on va également rajouter son nom dans le terminal comme ceci. Maintenant, on peut appuyer sur entrée pour ajouter ces trois composants dans notre application et on peut voir qu'on a une petite erreur parce que nous avons lancé cette commande à la racine de notre fichier au lieu de la lancer dans le dossier front-end. C'est toujours une erreur d'inattention que je que je fais. On va faire un cd front-end et on va relancer cette commande à l'intérieur et maintenant on peut voir que ça a très bien fonctionné. C'est en train d'installer nos trois composants.

42:37 Ces composants ont été ajoutés, donc nous allons maintenant modifier les imports pour pouvoir les utiliser. On se rend donc dans le fichier d'Abel et nous allons nous assurer que la fonction est bien appelée depuis le bon chemin ici. Pour la checkbox, c'est pareil et pour le text arrière, c'est pareil. Ça m'a l'air d'être bon. On peut donc retourner dans notre fichier forms ici et nous allons importer le label depuis u I slash label et notre textariat depuis u I slash text area.

43:04 On peut voir ici que camsea d'ads avait créé un composant qui s'appelle error list qui est similaire à notre ul ici. Donc, on peut retourner sur son replay théorie ici et nous allons l'utiliser car il est défini tout en haut du fichier. On le copie et on va le rajouter ici et nous allons l'appeler error list ici, il prendra le type de list of error. Et ce type, c'est tout simplement ce type là ici. Donc soit un tableau de string, soit undefined et nous allons copier le style que nous avions défini tout à l'heure pour cette liste comme ceci et nous allons supprimer le style de la liste pour rester cohérent avec notre design.

43:41 Maintenant, au lieu de boucler sur error masse dans notre composant field, nous allons à la place utiliser le composant error list que nous venons de définir en lui donnant comme argument nos erreurs. Maintenant, il nous reste un petit reacto à faire pour le deuxième composant qui s'appelle checkbox field. Nous allons donc importer les checkbox prod. Ici, donc on les importe et on peut les importer depuis ralliex UI slash react Red checkbox. Mais ce n'est pas ce qu'on veut effectivement.

44:08 Si on regarde dans le composant de Kent ici, on peut voir qu'il importe checkbox props depuis le composant de Shelsen UI ici. C'est-à-dire qu'il est allé dans le composant checkbox ici, sauf que quand on se rend nous-mêmes dans ce composant, on ne retrouve pas le type checkbox props. Donc ce qu'on va faire, c'est qu'on va retourner dans sa liste de composants. On va aller dans UI et dans checkbox pour copier le même type qu'il a utilisé lui, comme on a copié ses composants pour ne pas avoir de problème. Donc, on va copier cet export de type ici et nous allons le coller dans le composant de checkbox ici.

44:41 Il crée donc un type avec la primitive root et c'est ce type-là que nous exportons que nous allons maintenant importer dans notre fichier pour être vraiment cohérent. Maintenant que c'est fait, nous devons aussi importer un nouveau hook qu'on importe depuis conforme to React qui s'appelle use input Control et nous pouvons maintenant importer le composant de checkbox depuis UY slash checkbox comme ceci. Maintenant que c'est fait, on peut retourner sur notre formulaire ici myservice point oforidy et nous allons inverser le prix et la description de l'offre pour des raisons de design et nous allons à la place, au lieu d'utiliser le composant field, utiliser le composant qui s'appelle textteria field ici que nous voulons de créer. Et ce composant prend également un label, mais il prend aussi un props qui s'appelle textteria props ici et nous allons donc devoir modifier ces props parce qu'ils attendent des types différents. Ici on utilisait la méthode qui s'appelait get input props, Mais à la place, nous allons devoir utiliser la méthode qui s'appelle get textteria props.

45:42 On importe également depuis conforme to react comme ceci et on n'a plus besoin de définir le type du formulaire parce que savons que c'est un texte arrière. On peut voir ici qu'on a un petit problème avec ce props là. Ici, si on regarde dans le composant, on peut voir qu'il attend un prop qui s'appelle label props. Sauf que nous, nous l'avons appelé la belle props avec un s. Donc, nous allons tout simplement retirer le s et cela devrait être bon.

46:07 Maintenant, on peut rouvrir à nouveau le terminal, faire un cd double point pour retourner la racine de notre application et lancer la commande NPM rundev. Maintenant, on va rafraîchir notre page et on peut voir qu'on a bien nos composants qui ont été modifiés. Sauf que le label du textariat est différent du label de notre feed input. Et ça peut vous poser problème d'avoir un design pas cohérent. Effectivement, tout à l'heure, on avait créé le composant Field assez rapidement et il ne correspond pas au composant Field de Can't Sea Dogs.

46:36 Donc ce qu'on peut faire, c'est qu'on peut retourner sur son repository dans le fichier Forms en TFX et nous pouvons copier exactement le même composant comme ça on utilisera également le label depuis shell CNUI au lieu d'utiliser un label natif. Donc c'est ce que nous allons faire. Nous allons supprimer ce composant et nous allons copier celui de cancyddocks à la place avec notre type d'erreur qui est similaire à celui-ci comme ceci. Et maintenant, si on actualise la page, on peut voir que les labels ont entièrement disparu et c'est parce que nous avions fait une petite faute de frappe ici en appelant la propriété labels props au lieu de l'appeler label. Donc, on va tout de suite refactoriser ce composant et ce qu'on peut même faire, c'est qu'on peut dans le terminal lancer la commande NPM run tape check pour voir tous les fichiers qui possèdent des erreurs de type comme celle-ci.

47:25 Et on peut voir que nous avons deux fichiers. Nous avons la route register ici, donc nous allons modifier cette erreur dans les trois inputs qu'il utilisait et nous avons également la route lobbying ici qui utilisait notre inputs de type fil. Donc, nous allons également faire ce refacto à cet endroit. Si maintenant on relance la commande npm run ipt check, on peut voir qu'on n'a plus aucune erreur de type. On peut donc supprimer ce terminal et se concentrer sur la suite.

47:49 Maintenant, ce qu'on va faire, c'est qu'on va rajouter la logique côté serveur pour créer cette nouvelle offre. Donc, nous allons décommenter notre action ici, comme ceci, et nous allons modifier la logique. Déjà, cette validation ne sera plus asynchrone, donc on va pouvoir retirer ce super resain avec toute cette logique comme ceci. La validation n'est pas asynchrone, donc on peut retirer le mot clé async. Nous allons laisser ce code qui signifie qu'en cas d'erreur de validation, on renvoie une erreur qu'elles sont avec les données sous forme d'erreur.

48:19 Mais en cas de succès, nous allons pouvoir utiliser le service MX et le service Prizma pour créer une nouvelle offre. Pour ce faire, nous allons utiliser l'objet offert et nous allons utiliser la méthode create qui va prendre plusieurs paramètres. Pour plus de lisibilité, nous allons ajouter cette logique dans une fonction. Donc, nous pouvons retourner dans le fichier profile. Serveur ici, et juste à côté de get UserOffer, nous pouvons créer une fonction qu'on va appeler createOffer qui sera asynchrone et qui va prendre un contexte.

48:52 Il sera également typé apploade contexte comme ceci et nous pouvons finir la fonction fléchée et nous allons coller le code que nous avions copié depuis l'autre fichier. Donc, nous allons faire un contexte point remix service point prisma point offert point create. Sauf que nous voulons également utiliser le schéma ici, donc le schéma Zoot que nous avions créé. Donc, pour le réutiliser, nous allons devoir l'exporter comme ceci. On peut l'exporter et maintenant, comme nous l'avons exporté, on peut le rajouter comme argument, un argument qu'on va appeler offert data.

49:25 Et nous pouvons utiliser notre schéma zod comme argument en important zod comme ceci et en faisant z point infer pour insérer le type de notre schéma avec un z point infer, type off et le nom du schéma. Nous allons maintenant importer. Maintenant que c'est fait, on peut voir que notre offert data contient le title, la description et le prix du service de l'utilisateur, ce qui sera beaucoup plus simple pour créer cette offre. Donc, nous allons rajouter la data suivante. En title, nous allons prendre offert data en title.

49:57 Pour la description et pour le prix, c'est la même chose. Sauf que maintenant, nous devons récupérer le user ID pour sauvegarder cette offre comme étant une offre créée par l'utilisateur actif. Pour ce faire, nous allons devoir connecter l'utilisateur à cette offre en utilisant le mot clé connect et en le connectant sur l'ID. Sauf que cet ID, nous allons la récupérer depuis la méthode require user. On peut donc déjà rajouter le troisième argument à la méthode created offer qui sera donc un user ID et ça sera un string.

50:26 On va pouvoir récupérer ici. Maintenant, si on retourne dans notre fichier myservices point ofer ID, au lieu d'exécuter tout ce code ici, même commenter le retour, nous allons importer la méthode qu'on a appelée create offer qui prend donc trois arguments, le contexte, le offer data qui est égal à submission en value, car c'est la donnée de notre formulaire après validation et enfin le user ID. Alors comment récupérer ce user ID Nous allons faire exactement la même chose que dans le loader ici, c'est-à-dire qu'on va valider l'utilisateur et sa session dans le loader, mais nous devons aussi le valider en cas de poste ici. Donc, on va réutiliser cette méthode en cas de poste et nous allons sauvegarder la session de l'utilisateur dans une variable ici pour récupérer son user point ID. Maintenant que c'est fait, on peut retourner dans le fichier qui s'appelle create offer ici et ce que nous voulons faire, c'est renvoyer l'offre qui a été créée.

51:21 Pour ce faire, on peut supprimer tous les commentaires ici et nous allons maintenant, comme nous renvoyons la donnée après la création de cette offre, sélectionner ce que la base de données va nous renvoyer et nous souhaitons qu'elle nous renvoie l'ID de cette offre et aucune donnée supplémentaire. Maintenant, après la création d'une offre, cette méthode nous renvoie son ID. Et ce que nous pouvons faire, c'est que nous pouvons déstructurer. Ce qu'elle nous renvoie pour récupérer l'ID. Nous allons appeler created offer ID et nous allons maintenant faire une redirection sur la même page.

51:50 C'est-à-dire qu'actuellement, nous sommes sur la page my-services new. Et new, c'est l'ID d'une nouvelle offre. Sauf que maintenant, une fois que nous avons créé une offre, nous allons rediriger l'utilisateur sur la page my-services slash ID qui a été créé. Et cette fois, ça va être totalement différent parce que c'est l'ID de la nouvelle offre qui a été créée et nous allons faire un truc assez futé, les amis. Nous allons utiliser le même formulaire qu'on a utilisé pour la création de notre offre et nous allons l'utiliser pour l'édition de notre offre.

52:23 C'est pourquoi nous allons tout de suite renvoyer l'utilisateur vers la page my-services slash created offert ID comme ceci. On va tester tout de suite, voir comment ça marche. On peut donc supprimer tous nos commentaires et nous allons créer notre première offre. Là, on va actualiser la page. On est toujours sur l'url slash new et maintenant, on va créer cette offre.

52:43 On va l'appeler tondre la pelouse. Le prix de l'offre, ce sera trente-cinq euros et la description sera je viens chez vous tondre la pelouse une fois par semaine. Maintenant, en cliquant sur créer cette offre, ça va exécuter notre formulaire. Côté client, ça sera donc un poste et ça va déclencher notre action et la validation du formulaire. En cas d'erreur de validation, on va bien sûr renvoyer toutes les erreurs ici et en cas de non-erreur, on va créer une nouvelle offre et on redirige l'utilisateur vers cette nouvelle offre.

53:13 Donc, on va tout de suite essayer. Je vais cliquer sur créer cette offre et là, on peut voir qu'on a été redirigé sur l'offre qui s'appelle Kial ID clw trois h v. Maintenant, on a fait qu'une seule partie du boulot parce que cette offre, on aimerait pouvoir avoir ces informations dans le formulaire ici. Si on retourne sur la liste des services, donc sur la page ici, on peut voir qu'on a maintenant deux offres, une offre qui est active et une offre qui est inactive. Et on a bien les informations que nous avions rajoutées, c'est-à-dire tondre la pelouse trente-cinq euros.

53:45 Je viens chez vous tondre la pelouse une fois par semaine, publier le douze mai. Sauf que quand je clique sur cette offre maintenant, je ne peux pas pour le moment la modifier parce qu'on a seulement créé le formulaire de création d'offre. Sauf qu'il est possible avec Remix de créer ce formulaire et de conditionnellement créer ou éditer notre offre. Alors, comment est-ce possible Nous allons le faire tout de suite. Ici, nous sommes dans le loader.

54:11 Et comme nous l'avons fait tout à l'heure sur la page others point dollar others ID, nous pouvons récupérer le paramètre de l'offre avec ce morceau de code. Donc, nous allons le faire tout de suite. On copie ce morceau de code parce qu'on a appelé notre fichier de la même manière avec le paramètre $over ID et nous allons le rajouter comme ceci. On a donc besoin de récupérer le paramètre qui s'appelle paramètre. Maintenant, on peut console loguer le paramètre offert ID.

54:36 Et on va voir ce que la console nous affiche. Ici, ça nous affiche bien l'ID de l'offre qui existe. Mais si nous avions mis l'ID new, ça nous aurait affiché l'ID new ici. Sauf que c'est ce que nous voulions regarder. Ici, on va ajouter un morceau de logique.

54:51 Si l'offert ID est égal à new, alors nous allons renvoyer du JSON avec une offer qui est égal à nulle, comme ceci. Par contre, si l'offert ID n'est pas égal à new, alors nous allons renvoyer des JSON avec une offer qui n'est pas égal à nulle. Mais ça sera égal à un objet qui va contenir les informations de notre offre. Pour ce faire, on doit rajouter cette méthode qu'on n'a pas encore créée côté serveur. Donc, on va retourner dans notre profil point serveur ici et nous allons dupliquer la méthode qu'on a appelée getuser offer.

55:22 On la copie tout en bas et nous allons l'appeler getuser offer et nous allons ajouter un troisième argument qui s'appellera offert ID sous forme de string. On va donc le récupérer comme ceci et maintenant, nous voulons faire un find unic sur l'ID qui s'appelle offert ID. Sauf que dans Prisma, nous avons appelé ce champ ID ici. Sauf que ce que nous pouvons faire, c'est rajouter comme deuxième paramètre dans les conditions le user ID à cet endroit. Et c'est ce que nous avons décidé de faire.

55:50 Donc ici, nous renvoyons tout simplement l'offre qui existe dans la base de données à l'utilisateur. Et maintenant, nous pouvons appeler la méthode getUserUffer dans notre fichier qui n'est pas celui-là, mais qui est le fichier my tiret services point dollar offert ID. Et nous allons donc faire un await de cette méthode qui prend trois paramètres le contexte, le user ID et l'offer ID. Le for ID, c'est le nom du paramètre et le user ID, on le récupère avec la méthode require user, comme ceci. Maintenant, on renvoie donc bien le user ID et côté client, on reçoit donc un objet JSON qui contient soit une offre qui est égale à nul, soit une offre qui est égale à get user offer.

56:30 Sauf qu'on va rajouter un morceau de logique supplémentaire ici. Nous allons sauvegarder cette méthode dans une variable ici, parce que cette offre peut être nulle. Et si elle est nulle, alors nous voulons faire autre chose. Si l'offert n'existe pas, alors nous allons effectivement rediriger la page vers My tiret Services parce que ça ne devrait pas être d'origine. Maintenant, on peut utiliser la syntaxe courte ici et renvoyer le JSON qui contient notre offre et on peut être sûr que cette offre n'est pas nulle.

56:58 Maintenant, on va faire un essai. Du coup, en affichant la page slash new, on arrive bien sur ce formulaire. En affichant une aide au hasard par exemple, que j'ai créé, on est redirigé sur my service, ce qui est ce qu'on voulait. Par contre, si on clique sur une offre qui existe ici, on se retrouve sur la bonne page à la bonne ID. Ce qui signifie que cette page ne se charge seulement si l'utilisateur est connecté ou si on accède à une offre qu'il possède ou si on en accède à une nouvelle offre qui aura donc comme ID new.

57:29 Maintenant, ce qu'on a envie de faire, c'est de pré-remplir les champs de notre formulaire parce que nous sommes actuellement en train de regarder une offre qu'on a déjà créée. Donc, comment ça se passe côté client Eh bien, ça se passe de la manière suivante. Nous allons utiliser le hook qui s'appelle use loader data. Nous allons lui assigner le type générique à l'époque loader. Et nous pouvons maintenant récupérer la variable offer qui est soit nulle, soit égal à toutes ces données.

57:53 Sauf que nous, on a également besoin dans notre message ici de modifier le titre des boutons si c'est une nouvelle offre. Heureusement, nous pouvons le faire de la manière suivante. Nous pouvons utiliser le hook use params comme ceci et déstructurer le paramètre d'URL qui s'appelle offert edit. Maintenant, on peut rajouter un affichage initial. Si offert edit est égal à new, alors nous ajoutons le texte ajouter une offre.

58:17 Sinon, nous allons afficher le texte offre et le et l'aide de l'offre comme ceci. Donc là, on peut voir, on est bien sur le détail de notre offre. Ça commence bien, nous allons rajouter cette même condition ici ailleurs et nous pouvons même la sauvegarder dans une variable qu'on va appeler is new est égal à other ID est égal new maintenant on peut utiliser cette condition ici et nous pouvons la réutiliser pour afficher le bouton si c'est nouveau alors nous allons ajouter le texte créer cette offre Sinon, on peut rajouter le texte, mettre à jour cette offre comme ceci. On peut voir que le bouton, on prend bien qu'on est sur une offre qui existe déjà. Ce qu'on peut même faire, c'est rajouter un variant conditionné ici.

58:56 Si c'est une nouvelle offre, alors nous voulons du prix marie, mais sinon nous voulons du second arrêt. Alors ce qu'on va faire, c'est qu'on ne va pas utiliser second arrêt, on va utiliser le blue outline comme ceci pour montrer qu'il y a bien un changement entre les deux interfaces. Et maintenant, la dernière chose que nous voulons faire, c'est pré-remplir ses inputs avec conforme. Et pour ce faire, on va utiliser la propriété dans le hook use form qui s'appelle Default value et ça prend un objet et cet objet, il contient les trois propriétés de nos inputs. Le title, ça va donc être offert point title si l'offre existe, sinon ça sera égal à nul.

59:32 Pour la description, c'est pareil et pour le prix, c'est pareil. Si on actualise la page maintenant, on peut voir que les informations de notre annonce ont été préremplies sur le même formulaire. Ce qui signifie que l'utilisateur, il peut maintenant modifier cette annonce. Mais regardez ce qui se passe s'il modifie cette annonce. On va donc renommer le titre de l'annonce, on va on va l'appeler Andrew l'herbe et si on met à jour l'annonce, qu'est-ce qui va se passer ici C'est qu'on est redirigé sur une nouvelle annonce.

59:60 Et si on retourne sur notre liste d'annonces ici, on peut voir qu'on a maintenant trois annonces tondeuse à gazon, tondre la pelouse et tondre l'herbe. Et bien, c'est parce que notre action a créé une nouvelle offre au lieu de d'éviter de modifier cette offre-là. Et c'est pour la simple et bonne raison que nous n'avons pas encore utilisé un chargement conditionnel comme ici. Ici, nous déterminons qu'une offre est nouvelle avec son paramètre du r l et nous devrions faire exactement la même chose dans notre action. Et c'est ce que nous allons faire tout de suite.

60:29 Nous allons copier ce morceau de code ici dans notre action comme ceci et nous allons récupérer l'objet Params et nous allons nous allons créer une nouvelle offre seulement si le paramètre dans l'URL est égal à new comme ceci et on peut déplacer ce code à cet endroit et supprimer ce code à cet endroit. Donc, si le paramètre de l'url est égal à new, alors nous allons créer une offre et nous allons rediriger l'utilisateur vers l'offre qu'il vient de créer. Par contre, si ce n'est pas le cas, alors nous sommes dans la deuxième condition et nous allons rajouter un code bien différent. On pourra donc renvoyer par exemple nul et ici, on peut utiliser une méthode qu'on va appeler edit offer. Cette méthode n'existe pas et nous allons la créer tout de suite en nous inspirant du create offer.

61:15 On va donc copier la méthode create offer et on va la renommer en edit offer, ça va prendre la même donnée ici et au lieu de faire un create, nous allons faire un update comme ceci. Il nous faut bien sûr une condition, donc on peut réutiliser cette condition-là. On avait plus haut pour la récupération de notre offre et cette méthode prend un troisième paramètre qui sera le offert ID, forme de string. On va récupérer comme ceci. Bien sûr, on n'est même pas obligé de renvoyer le type de retour.

61:44 Donc maintenant, on va utiliser la méthode qu'on appelle Edit User qui prend en contexte l'offer data, le User ID et ça prend troisième paramètre qui s'appelle le qui est donc le paramètre d'URL. Mais au lieu de déstructurer l'ID de l'offre, on exécute simplement cette fonction et on renvoie nulle. Maintenant que c'est fait, on peut retourner sur cette page et nous voulons modifier cette offre, tondre la pelouse et nous allons faire un effet. On va remplacer le prix, on va mettre quarante-cinq euros et si on clique sur mise à jour ici, on peut voir qu'on n'a pas changé de page. On est toujours sur la même page.

62:18 Et si on actualise la page, on peut voir que le prix reste à quarante-cinq euros. Si on appelle l'annonce tondre comme ceci et qu'on met la page à jour, on peut voir que c'est exactement pareil pour tous les champs. Et si on retourne sur la liste de nos annonces, on peut voir que la dernière annonce ici, c'est notre annonce qui s'appelle tondre et qui coûte quarante-cinq euros. En fait, c'était la deuxième annonce, mais comme elle a été modifiée ensuite, Prizman nous l'affiche en dernier. Et nous pouvons éviter cela en rajoutant un morceau de logique dans notre page my services point index ici et nous pouvons faire un order by sur le champ created at en ascendant comme ceci.

62:55 Maintenant, on peut voir que la première offre, c'est tondeuse à gazon et la deuxième, c'est tondre et ça ne changera jamais parce que la date de création n'est pas modifiable. Maintenant, ce qu'on a envie, c'est de rajouter une option dans le formulaire d'édition de notre offre pour la rendre active ou inactive. Pour ce faire, on va donc cliquer sur notre première offre ici et nous allons retourner dans notre fichier qui s'appelle my services point dollar other ID. Et dans ce formulaire, nous pouvons rajouter un nouveau champ à notre schéma zone, une nouvelle propriété que nous allons appeler active qui sera donc égal à booléan et nous pouvons même rajouter une valeur par défaut qui sera égal à false, sous-entendu que l'offre sera par défaut inactive. Maintenant que ce champ a été rajouté, nous allons tout en bas dans notre composant utiliser le nouvel input que nous avons rajouté tout à l'heure qui s'appelle checkbox field comme ceci.

63:51 Et donc cet input prend plusieurs paramètres. Il va prendre un premier props qui s'appelle bouton props et on peut voir que ce bouton props prend deux paramètres obligatoires le forme qui est donc l'ID de notre formulaire et le name qui est le nom de notre propriété. Ce qu'on peut faire à la place si réutiliser les props nous récupérons ici depuis la méthode get input props. On va pouvoir utiliser cette méthode get input props sur la propriété active et nous allons mettre que le type et de type checkbox comme ceci. Maintenant, il nous suffit juste de rajouter un label comme pour les autres inpots qui sera égal à activer cette offre par exemple.

64:34 Maintenant, on va également rajouter les erreurs et nous retrouvons notre composant de checkbox côté client. Alors, à quoi ça ressemble côté serveur On va tester ça tout de suite. On va retourner dans notre action ici et ce qu'on a envie de faire, c'est de faire un console log sur la submission point value, mais on va la faire après la validation, c'est-à-dire ici et nous allons console loguer la valeur active. On va même pouvoir la console loguer en tant qu'objet comme ceci. Pourquoi fait-on ça En fait, on aimerait connaître la valeur de la checkbox si elle n'est pas cochée.

65:06 On va ouvrir notre terminal tout de suite, notre terminal côté serveur de notre application remix et nous allons cliquer sur mettre à jour cette offre. Et là, on peut voir qu'on a une valeur de false au statut active. Par contre, on coche la checkbox et qu'on remet à jour cette offre, on peut voir que le statut passe à True. Mais si on fait un clic droit sur notre page ici et qu'on se rend dans l'onglet network et qu'on renvoie une soumission de formulaire ici, on clique bien sur la requête et dans le payload, on peut voir que le active est égal à on et pas à true. Et si on clear les éléments, donc si on supprime tous les log de notre console, qu'on désactive la checkbox et qu'on reclique pour soumettre le formulaire à nouveau ici et qu'on clique dessus, on peut voir qu'on ne retrouve plus du tout la propriété active.

65:58 Effectivement, c'est conforme qui se charge de transformer la valeur de la checkbox au moment de la validation en utilisant zod. Du coup, l'absence de checkbox fera qu'elle est égale à false et la présence de cette checkbox fera qu'elle est égale à trop. Par contre, une question que je me pose, c'est que se serait-il passé si on n'avait pas mis le à false ici Personnellement, je ne suis pas sûr. Je pense que ça aurait mis une erreur en dessous de la checkbox parce qu'elle n'est pas envoyée côté client. On peut faire un essai.

66:29 On va donc envoyer notre formulaire sans activer notre checkbox ici et on peut voir qu'on a effectivement donc une erreur au niveau de notre formulaire parce que ce champ est obligatoire. Sauf que, on a décidé de ne pas activer cette checkbox, donc c'est voulu que ça ne soit pas obligatoire. C'est pourquoi nous devons rajouter un default pour les inputs de type checkbox et dans notre cas, la valeur par défaut est égale à false. D'ailleurs, pour toutes les checkbox, la valeur par défaut devrait être égale à false parce que ça sous-entend que ça serait relié à cet input qui ne possède une valeur seulement si la checkbox est activée. Ce qu'on peut rajouter quand même, c'est un message d'erreur au cas où, on ne sait jamais, votre annonce doit être active ou non.

67:14 Et maintenant que c'est fait, on peut modifier notre méthode d'édition. On va donc retrouver la méthode edit offer ici et nous voulons rajouter la nouvelle propriété active qui est égal à offert data point active. Et on peut voir que notre propriété offert data possède bien une clé active qui s'est rajouté quand on a modifié notre schéma Zod. C'est exactement ce qu'on voulait, c'est vraiment super. Maintenant, en ce qui concerne la création d'une nouvelle offre, ce qu'on a envie, c'est de ne pas afficher cette checkbox lorsqu'on crée une nouvelle offre, tout simplement parce qu'on aimerait que l'utilisateur vérifie une nouvelle fois son annonce avant de pouvoir la publier.

67:54 Donc, nous allons faire un affichage conditionnel avec le booléen que nous avions utilisé plus haut, is new, et nous allons mettre que si c'est une nouvelle offre, alors on ne va rien afficher, sinon nous allons afficher notre checkbox. Maintenant, on peut faire un essai. On va retourner sur la liste de nos annonces sur la route My Services comme ceci et nous allons ajouter une nouvelle annonce. Et là, on peut voir que la checkbox n'apparaît pas. Maintenant, si on rajoute une nouvelle offre jardinage vingt-trois euros, je jardine et qu'on appuie sur créer l'offre, on peut voir qu'elle a été initialisée avec un statut active à false.

68:28 Et ça tombe bien, c'est seulement lors de l'édition qu'on peut activer cette offre. Maintenant, on va activer cette offre et on va de nouveau retourner sur notre liste des dernières offres et on peut voir que jardinage est une offre qui est active. Donc ça marche très bien. Nous allons maintenant supprimer notre console log côté serveur. Pour le moment, ça va nous suffire.

68:48 Ce qu'on a envie de faire maintenant, c'est de pouvoir en tant qu'un autre utilisateur, faire une proposition, donc créer une transaction avec l'utilisateur propriétaire de l'offre. Donc pour ce faire, nous allons nous déconnecter et nous allons cliquer sur cette nouvelle offre qui vient d'apparaître, qui s'appelle jardinage. On va cliquer dessus et on se rend donc sur le détail de cette offre et ici, nous aimerions ajouter une action utilisateur. Pourquoi pas un bouton pour que l'utilisateur puisse envoyer une demande de transaction au propriétaire de ce service. Nous allons donc tout en bas ici et nous allons rajouter notre bouton.

69:26 On va importer depuis notre librairie Chelsea new eye. L'action sera primary et nous allons ajouter un texte. Par exemple, cette offre m'intéresse. Bien sûr, on ne va pas oublier de rajouter un class name avec un Wegs en fit pour que le bouton ne soit pas trop large et nous allons finalement le placer dans notre article pour qu'il puisse être au même niveau en termes de padding. Ce bouton va appeler une nouvelle route et la route, on ne l'a pas encore créé, on va la créer tout de suite.

69:53 On va donc ouvrir notre explorateur de fichiers dans le dossier route ici dans public et nous allons créer deux root. La première route s'appellera transactions au pluriel, donc transactions point TFX, mais nous pouvons l'appeler transactions point index point tf x, car ce ne sera pas une route imbriquée et la deuxième s'appellera transactions point dollars transaction a b point tf x. Pour le moment, nous allons renvoyer un loader qui va renvoyer nulle dans les deux routes comme ceci. Maintenant, qu'est-ce qui va se passer On va retourner dans le fichier other's point dollar otheridy et c'est à cet endroit que nous allons rajouter une nouvelle action. Donc, on va pouvoir exporter une constante qu'on va appeler action qui sera une fonction asynchrone et qui renvoie pour le moment nul.

70:42 Nous allons également récupérer les arguments dans le action function args, sauf que cette action ne pourra être exécutée seulement si l'utilisateur est identifié. Donc, nous allons utiliser un await requiere user comme ceci, en passant comme paramètre le contexte. On n'oublie pas de rajouter dans les paramètres de notre action. Sauf que si nous faisons un await requiere user et qu'on n'est pas identifié, ça va nous rediriger vers la page login. Et si ça nous redirige vers la page login, on aura perdu les informations de cette offre.

71:16 On ne saura plus quelle offre c'est. Donc ce qui aurait été pas mal, c'est à la place de faire une redirection, d'un redirect quand même sur la page login, mais avec un champ de redirection qui ramène l'utilisateur une fois connecté sur cette page. Donc on peut voir ici que cette page, c'est offers slash et l'ID de notre route et on aurait voulu rajouter un champ, par exemple le redirect 2 qui serait donc un paramètre du rail et qui serait égal à la route sur laquelle on était précédemment. Donc, offers ID de l'offre. De cette manière, en cliquant sur le bouton cette offre m'intéresse quand on est déconnecté, ça va nous rediriger sur le formulaire de connexion et une fois qu'on clique sur se connecter, au lieu de nous rediriger vers la page d'accueil, ça nous redirigerait à nouveau sur le détail de cette offre.

72:06 Et c'est ce qu'on va faire tout de suite. Du coup, on va cliquer sur la méthode redirect Uther et nous allons ajouter un deuxième champ, donc un deuxième argument à notre fonction qu'on va appeler redirect to et ça sera un string. Et notre redirect to aura comme valeur par défaut la valeur de slash parce que par défaut, après une connexion réussie, on redirige l'utilisateur sur la page d'accueil. Donc maintenant, ce qu'on a envie de faire, c'est de récupérer ce paramètre redirect to et on a envie de le rajouter ici sur cette url. Donc, on va rajouter ce paramètre redirect to est égal à la variable redirect 2 comme ceci.

72:45 Maintenant, une fois qu'on est redirigé sur la page login point TFX à cet endroit-là, si on retourne dans notre action, à ce niveau-là, on peut récupérer ce paramètre d'url à cet endroit. Et on peut voir que dans la route login, une fois qu'on a identifié l'utilisateur, on effectue une redirection vers la route authenticate avec un champ qui s'appelle token et le token d'authentification. Et ce qu'on peut faire ici, c'est de rajouter notre deuxième champ redirect to qui prendra comme valeur redirect to. On n'a pas encore récupéré et qu'on peut tout de suite récupérer avec un request point URL. Effectivement, alors Copilot, c'est déjà ce que j'ai envie de faire parce que je l'ai fait de nombreuses fois, mais une bonne pratique est de recréer des paramètres d'URL en faisant un new url du request ou un URL et ensuite avec ce nouvel objet url que nous venons de créer, nous pouvons récupérer les paramètres qui s'appelle search Params, comme ceci.

73:46 Maintenant, on peut assigner à la variable redirected to un paramètre, la valeur du paramètre redirected to ou alors un slash comme ceci. Maintenant que c'est fait, on va faire une dernière chose. On retourne donc sur le détail d'une offre et à cet endroit, ce qu'on a envie de faire, c'est de rediriger l'utilisateur sur cette url. Donc, au lieu d'effectuer une redirection à ce niveau-là, nous allons passer le deuxième argument que nous venons de créer, redirecte to qui sera égal à slash others slash others ID. Et ce others ID, on le récupère ici en utilisant le paramètre d'url comme ceci.

74:23 D'ailleurs, nous pouvons supprimer ce console log qui ne nous est plus utile. Donc, on n'oublie pas de récupérer les paramètres depuis notre action comme ceci. Et maintenant, en cas de non authentification de l'utilisateur, il est censé être redirigé sur la page login avec un paramètre d'URL redirect to qui est égal à slash offert slash offert ready. On va tester ça tout de suite. On peut voir ici, on est déconnecté.

74:48 La preuve, les boutons connexions et inscriptions sont affichés. Maintenant, si on clique sur cette offre m'intéresse, déjà c'est un bouton. Alors on va changer ça tout de suite. Au lieu d'être un bouton, on va rajouter un formulaire, on importe depuis Remix, avec une méthode post qui est obligatoire pour qu'on puisse exécuter cette action. Et pour le moment, c'est tout ce qu'on va faire.

75:07 On n'a pas besoin de faire plus. Donc, on a un formulaire et un bouton. Le bouton sera de type submit. On ne va pas oublier de le préciser. Et maintenant, si on clique sur cette offre m'intéresse en étant déconnecté, c'est censé exécuter notre action.

75:21 On va tester, je vais cliquer tout de suite. Ça n'a pas marché la première fois, j'ai donc eu besoin de rafraîchir la page et maintenant, j'ai cliqué sur cette offre m'intéresse et ça m'a ramené sur la page login et sur la route, slash chauffeurs slash et l'ID de notre offre. Le souci, c'est qu'on n'a pas encore ajouté cette logique dans notre application Nest. Js. Donc, nous allons retourner dans notre fichier qui s'appelle hausse point controller à cet endroit-là et ici, nous avons donc la méthode authenticate et effectue une redirection vers slash.

75:53 Ici, on va donc rajouter un morceau de logique à notre méthode login. Tout d'abord, nous avons besoin de récupérer des paramètres, comme pour notre méthode logout et nous allons utiliser le décorateur qui s'appelle query. Car nous allons récupérer un paramètre d'URL que nous avons appelé redirect-to qui sera donc un string que nous allons appeler redirect-to. Et ce qu'on a envie de faire pour le moment, c'est de faire un console log de cette variable qui s'appelle redirect-to. 2.

76:20 Maintenant, comme vous pouvez le voir, on a ce décorateur qui s'appelle redirect et qui renvoie sur la page index. Et ce n'est pas ce qu'on a envie. Nous, on a envie de renvoyer sur l'url redirect 2 et il y a un moyen de le faire avec Nest. On tape sur la documentation arobase redirect Nest JS comme ceci et qu'on clique sur contrôleur, on peut retrouver dans la documentation en faisant une recherche sur le décorateur redirect des informations supplémentaires. Sauf que ici, ils nous disent bien que Return value's wheel override and argument pass to décorateur redirect.

76:56 Et il nous donne exactement l'exemple que nous voulions. Ici, on récupère un paramètre d'URL, donc un query parameter et s'il matche la version cinq, alors on effectue une redirection particulière. On effectue une redirection sur une URL qui est différente et on renvoie un objet qui contient une clé URL et une valeur. On va faire exactement la même chose, donc nous allons renvoyer une url qui sera égale à redirect do comme ceci. Maintenant, on va tout de suite tester cette logique.

77:27 On est donc sur une page login avec une url de redirection sur une offre. On va donc se connecter à un compte qui ne possède pas cette offre, ABC un, deux, trois. On clique sur se connecter et là, on peut voir qu'on est sur notre nouvelle URL, sur l'URL de l'offre sur laquelle on était juste avant. Et si on ouvre la console ici, on peut voir le console log, redirect 2 et la bonne URL qui a été console loguée dans notre contrôleur Nest. C'est exactement ce que nous voulions.

77:59 Maintenant, si on clique à nouveau sur le bouton cette offre m'intéresse, on ne sera plus redirigé sur la page login. On va essayer tout de suite, on va ouvrir le network, on va cliquer dessus et là on peut voir qu'il y a des requêtes qui sont faites, mais il n'y a rien qui ne se passe. C'est parce qu'on n'a pas encore ajouté la logique supplémentaire. Donc ici, on va retourner dans notre offert point au dollar offert ID et à ce niveau-là, après avoir require l'utilisateur, ici, on est sûr que l'utilisateur est identifié. Maintenant, on va effectuer une deuxième vérification.

78:33 Nous allons récupérer les informations de cette offre et on veut s'assurer que l'utilisateur connecté n'est pas propriétaire de cette offre. Donc, nous allons copier ce code-là ici de cette manière et nous allons faire un if poundoffer. Point user a d est égal à notre utilisateur, alors nous allons renvoyer une erreur. Sauf que, comme on peut le voir dans le select, on ne renvoie pas les informations de notre utilisateur. Donc, au lieu de rajouter cette logique précisément, nous allons plutôt créer une fonction avec une logique à part entière.

79:07 Et nous allons maintenant dans le fichier offert en serveur et nous allons créer une nouvelle méthode qu'on va appeler create transaction qui va prendre plusieurs paramètres. Le premier paramètre, ça sera donc l'ID de l'offre et le deuxième paramètre, ça sera l'ID de l'utilisateur qui se connecte ici. Donc, on a besoin de récupérer les informations de l'utilisateur qui se connecte, notamment en ID. On va donc ajouter un paramètre user ID à ce niveau-là, comme ceci. Et maintenant, ce qu'on peut faire, c'est qu'on peut récupérer les informations de cette offre.

79:39 Le premier check sera de vérifier que cette offre existe bien. Ensuite, on a envie qu'elle soit active. Donc, on va mettre une condition where active est égal à trois. Maintenant, dans le select, ici, on veut récupérer le user ID. Maintenant, on peut supprimer les autres paramètres de ce select là.

79:58 Nous pouvons rajouter une condition, if. Déjà, si l'offre n'existe pas, alors on peut renvoyer une erreur, impossible de trouver l'offre. Deuxième condition, si l'offre existe et que le user ID est égal au user ID de l'utilisateur identifié, alors dans ce cas-là, une erreur de type vous ne pouvez pas acheter votre propre offre. D'ailleurs, il y a quelque chose qu'on n'a peut-être pas fait. Je vais regarder parce que ça me titille un petit peu.

80:25 Ici, on va donc aller dans le backend et on va ouvrir Prima Studio, mais je crois on peut accéder à toutes les offres, même celles qui sont inactives. On va prendre par exemple la deuxième offre qui s'appelle tondre. On peut voir ici qu'elle est bien inactive. Mais si on récupère son ID et qu'on essaye d'accéder à cette offre sans être le propriétaire, on peut voir qu'on arrive à y accéder. Et ça, c'est une faille de sécurité vu que cette offre n'est pas active.

80:51 Donc, on peut refermer Prima. On a confirmé notre doute et ici, dans la méthode Loader, on a envie de rajouter une condition if fund lofer point active est égal à false. Alors, dans ce cas-là, on va faire un throw de la méthode re direct et on va rediriger sur la page d'accueil. Et pour ce faire, on a besoin quand même de récupérer le statut active ici. On va retourner ici et on peut le faire de deux façons.

81:16 Soit on rajoute la propriété active dans le select, soit une autre façon, c'est de rajouter la propriété active dans le where ici, ce qui nous épargnerait toute cette logique. Maintenant, on peut voir qu'on a cette erreur-là qui est un peu différente de l'erreur qu'on avait prévue. Au final, je pense que je préfère afficher un message d'erreur plutôt que de rediriger l'utilisateur sur la page d'accueil. Donc, c'est parfaitement ce que j'avais envie de faire. Maintenant, on va cliquer sur back pour retourner sur notre offre qui est bien active et on va retourner dans notre offers point serveur ici pour terminer de créer notre transaction.

81:49 On récupère donc le détail d'une offre, donc le user ID et l'ID. Si l'offre n'existe pas, on renvoie une erreur. Si l'utilisateur qui essaye de commander cette offre est l'utilisateur connecté, on renvoie également une erreur. Sinon, alors on va pouvoir utiliser Prima et nous pouvons créer une nouvelle transaction. On va donc faire un Prima point transaction point create et nous allons rajouter les données suivantes.

82:14 On va donc connecter notre offre avec un connect sur son ID comme ceci. Ensuite, on va rajouter la donnée supplémentaire. Donc l'utilisateur, il vient de créer cette transaction, c'est bien l'utilisateur qui est connecté. On va aussi bien refermer les moustaches. On a donc connecté l'offre.

82:31 On a également connecté l'utilisateur et c'est tout pour le moment. Sauf que ça me fait penser, on a besoin de faire un dernier check. On a envie de vérifier que cet utilisateur n'a pas déjà créé une transaction pour cette offre. D'ailleurs, c'est quelque chose qu'on aurait pu rajouter dans notre schéma Prizma. Ici, on peut voir qu'on a le user ID et l'Ofer ID Et normalement, notre utilisateur peut proposer, donc créer une transaction pour une offre et il a le droit qu'à une seule transaction pour une offre.

83:01 Il ne va pas pouvoir créer plusieurs propositions. Il peut créer qu'une seule transaction. Et ce qu'on aurait pu faire pour nous assurer que cette transaction est bien unique, c'est rajouter un champ unique ici avec un double arobase unique et un tableau sur les champs over ID et user ID. Donc pour le moment, on va le rajouter. On va ouvrir un deuxième terminal, on va faire un CD dans Back and et nous allons faire un NPX Prima migratedev avec comme name unique user offer.

83:30 Maintenant, on nous avertit de notre changement, mais ce n'est pas grave, on n'a créé aucune transaction dans la base, donc on va accepter en appuyant sur y. Et voilà, maintenant, on peut fermer ce terminal et on peut être sûr qu'au niveau de la base de données, ça sera impossible de créer plusieurs fois la même transaction. Mais pour être sûr, on va quand même rajouter un dernier check ici en récupérant l'existiing transaction si elle existe et en faisant await, contexttreming service, prisma transaction, fine unique. Et comme on avait mis dans notre schéma Prima que le champ Offer ID et Yoser ID est unique, on peut maintenant ajouter une condition where sur notre champ unique Offer ID underscore Yoser ID parce que c'est une nouvelle propriété que Prizma a créé parce qu'on lui a bien dit que cette combinaison est forcément unique. Donc on va vérifier tout de suite qu'elle est bien unique.

84:23 Ce qu'on a envie, c'est que si cette transaction existe, alors on dise à l'utilisateur qu'il ne peut pas de nouveau recréer une transaction avec un message erreur, par exemple, vous avez déjà créé une transaction pour cette offre. Maintenant, on est sûr que toutes les sécurités ont été mises en place. Un utilisateur peut faire une offre seulement sur une offre qui est active, qui ne lui appartient pas et sur lequel il n'a pas déjà fait une transaction. Maintenant, ce qu'on a envie de faire, c'est qu'on va return la donnée qui a été créée, donc notre transaction et on va rajouter un select sur les propriétés ID. Pour le moment, on a seulement besoin de ID parce qu'on va effectuer une redirection sur cette page-là.

85:03 Et maintenant, on peut retourner dans notre page others point dollar othor ID et à ce niveau-là, on va pouvoir appeler notre méthode qu'on a appelée donc create transaction qui prend donc plusieurs paramètres. Notre contexte, le offert ID et le user ID est égal à user point ID. Maintenant, en cas d'erreur, la transaction ne sera pas créée et s'il n'y a aucune erreur, la transaction sera bien créée et il y a plusieurs manières d'arriver à ce résultat. Ici, on peut déjà déstructurer l'ID. Ce qu'on a envie de faire au final, c'est de renvoyer une redirection sur la page transaction dollars ID comme ceci.

85:42 Mais admettons qu'il y ait une erreur lors de la création de cette transaction. Ici, on peut voir qu'il y a plein d'erreurs qui sont possibles. Par exemple, l'offre n'existe pas ou alors elle a été désactivée ou alors l'utilisateur qui essaye de commander cette offre est le propriétaire de cette offre ou alors la transaction existe déjà. Alors qu'est-ce qu'on va faire ici Parce que si on appelle cette méthode et que l'une des trois erreurs est déclenchée, ça va faire crash notre site. Alors, qu'est-ce qu'on pourrait faire pour protéger notre site et afficher un petit message d'erreur en rouge à l'utilisateur pour lui dire mince, ce n'est pas possible pour toi de créer cette transaction.

86:18 Et bien déjà, pour s'en rassurer, on va tout de suite rajouter un message d'erreur pour être sûr que l'erreur est générée. Par exemple, not implémented. Comme ça, ça va nous permettre de vérifier qu'on est sur la bonne voie. Ici, du coup, on est connecté et on va cliquer sur cette offre m'intéresse, ce qui va déclencher cette action et ça va générer l'erreur. Voyons cliquer dessus et là, on peut voir qu'on a l'erreur.

86:42 On a donc annonce introuvable, semblerait que l'annonce que vous cherchez n'existe pas, ce qui n'est pas l'erreur qu'on voulait afficher. Mais ça, Remix ne le sait pas. En fait, on a utilisé ici le composant Error Bountry et ce qui est du coup pas du tout générique parce qu'on n'exploite pas vraiment les capacités de Remix. C'est vrai, il y a un hook qui s'appelle use root error et qui nous permet de récupérer les informations de notre erreur. Ici, par exemple, si on voulait afficher un div et cette erreur sous forme de string et qu'on recliquer sur le bouton cette offre m'intéresse, on pourrait voir qu'on a bien notre message d'erreur erreur not im play manted.

87:20 Mais on ne va pas faire comme ça. On aime bien cette erreur et on ne va pas pour le moment créer un composant error qui va afficher l'erreur peu importe l'erreur. En fait, ce qu'on aurait envie, c'est d'afficher le message d'erreur juste en dessous de notre bouton ici, au lieu de faire tout planter et de remplacer notre composant. Il y a plusieurs manières de le faire. Ce qu'on pourrait faire, c'est rajouter ici un try catch par exemple.

87:45 Si on rajoute un try catch à ce niveau-là, comme c'est fait, on aurait donc un message d'erreur sous forme de log et on pourrait le renvoyer côté client au format JSON comme ça. Maintenant, si on clique sur cette offre m'intéresse, ça ne fait plus cracher notre application. Par contre, on a besoin de récupérer côté client notre message d'erreur Et je ne suis quand même pas fan de cette méthode. En fait, j'aurais vraiment voulu pouvoir effectuer tout ce traitement qu'on a fait, mais pouvoir afficher le feedback à l'utilisateur. Et en fait, c'est possible, c'est totalement possible de le faire et je l'aurais fait personnellement avec la librairie Onform.

88:19 Alors, comment pourrait-on le faire avec Onform Déjà, pour utiliser Conform, il nous faut un schéma ZOD. Donc, on va créer un nouveau schéma qu'on va appeler create transaction schéma et on va appeler ZOD et on va créer un objet avec une clé qu'on va appeler action et en valeur un littéral qu'on va appeler create transaction comme ceci. Maintenant, ce schéma Zod, on a envie de le récupérer côté client. Donc, nous allons utiliser le hook use form et nous allons prendre comme constaint la méthode get Zod Construint de notre schéma Zod et on va bien sûr valider ce schéma zod côté client. Mais ce schéma zod, il n'aura aucune donnée dans son schéma.

89:01 En fait, on utilise juste zod par prétexte pour pouvoir afficher les erreurs avec conforme. Donc, on va renvoyer par souhait zone effet et c'est tout ce qu'on a besoin de faire. Maintenant, on va quand même laisser conforme gérer ce formulaire. Donc, on va récupérer les props ici et on va faire get form props et on va le déstructurer. Et maintenant, qu'est-ce qu'on peut faire pour notre bouton On peut faire deux choses.

89:24 Soit on pourrait prendre le field et le mettre en dessous et donner cette valeur au bouton, soit on pourrait rajouter un input de type idem et on pourrait utiliser get input props avec la valeur de notre action comme ceci. Mais on n'a même pas besoin de faire ça. Regardez, en fait, on a déclaré ici dans notre schéma ZOD un littéral et ce schéma, il accepte une seule valeur, c'est create tiret transaction. Donc, nous allons assigner à ce bouton un nom qui sera donc action et une valeur qui sera create transaction. Ça signifie que notre composant, il possède bien son nom et sa valeur.

90:01 Donc, il possède la bonne valeur. Donc, côté client, il n'y aura aucune erreur de validation conforme à bien valider la valeur de notre input. Maintenant, on a quand même envie d'afficher notre composant d'erreur qu'on avait appelé erreur list et qu'on va importer ici et nous allons importer les erreurs de notre formulaire en faisant un form point errors comme ceci. Maintenant, on peut arrêter de déstructurer la propriété field. Et ce qu'on a envie de faire, c'est de récupérer la propriété côté serveur parce que c'est la validation côté serveur qui va faire toute la différence.

90:32 Et cette validation côté serveur se retrouve dans le last result. Donc pour y arriver, on a besoin de faire comme si on validait un vrai formulaire. À ce niveau-là, on va donc devoir récupérer le form data avec un await request point form data. Il ne faut donc pas oublier de récupérer la propriété request comme ceci. On peut déplacer cette logique en dessous, comme ça, ça ne va exécuter cette logique seulement si l'utilisateur est connecté et donc on va récupérer la sommation avec par sur le form data et sur notre schéma.

91:05 Maintenant, on va ajouter le même code que sur nos autres pages. S'il n'y a aucune valeur, alors nous allons renvoyer le résultat au format de JZAN avec un seul mission point replay. Et ce code est exactement le même que dans la page my services point dollar offert ID à ce niveau-là ici. On renvoyait un seul mission point replay. On peut d'ailleurs copier cette logique pour nous éviter de la réécrire.

91:29 Sauf que le statut ici, il sera toujours à succès parce qu'on valide un input, sauf que cet input, on le sait tout le temps. Mais c'est là qu'on va être malin. Ici, dans notre schéma côté serveur, nous allons le raffiner, c'est-à-dire que nous allons le modifier et rajouter des conditions de validation supplémentaires en utilisant la méthode super refile qui accepte une fonction call back et qui nous donne deux paramètres la donnée et le contexte. Pour le moment, ce qui nous intéresse, ce n'est pas la donnée. Ce qui nous intéresse, c'est le contexte.

92:00 Donc, nous allons par exemple ajouter une issue avec comme code custom et comme message impossible de créer la transaction. Là, pour le moment, c'est une erreur pour de faux. C'est pour vous montrer l'esprit de ce qu'on essaye de faire. Ici maintenant, comme on a rajouté une validation côté serveur de notre input, si on soumet le formulaire et qu'on inspecte l'élément, là, on va cliquer sur soumettre le formulaire et on peut voir que la requête nous a renvoyé une erreur. On peut donc voir en statut erreur, impossible de créer la transaction.

92:30 Sauf que l'erreur n'apparaît pas côté client et c'est parce qu'on a oublié de rajouter le last result ici. Donc on va récupérer notre action en utilisant le hook use action data. On va d'ailleurs renommer cette variable action data et on va rajouter le type générique type of action comme ceci. Et donc notre last resolve serait égal à action data point resolve. Si on rafraîchit la page maintenant, on peut voir tout de suite le message d'erreur en dessous de notre bouton.

92:56 On va donc rajouter un petit peu le style et ce qu'on a envie de faire maintenant, au lieu de rajouter une erreur, c'est de transformer notre schéma ZOD en validation asynchrone parce qu'on a besoin d'appeler la base de données. Donc, on va ajouter le mot clé async true et nous allons faire un naweit de to parse with thed. Et maintenant, dans notre méthode create transaction, ici, on peut tout simplement copier toute cette logique de validation. Donc les trois messages d'erreur et ces trois messages d'erreur, on peut les rajouter. On On va bien rajouter le à ce niveau-là et on déplace en fait cette logique ici dans notre super refine.

93:36 Donc dans notre logique zod, on va copier le code ici et nous allons marquer le super effet comme prenons une fonction call back asynchrone et cette fois, au lieu de faire des, nous allons faire des add is you sur du code custom et des messages. Par exemple, impossible de trouver l'offre. Maintenant, si on a trouvé une offre, on n'a pas envie qu'elle soit égale à user point id comme ceci. Et maintenant, si on trouve une formation, du coup, si c'est égal, on va rajouter le même message d'erreur, mais avec conforme, on peut donc supprimer les straw parce que si on straw, ça va faire crash notre application et sur la dernière erreur, on va donc en cas de transaction qui existe déjà, afficher cette erreur-là et on peut supprimer le straw. Sauf que comme vous voyez, on a des petites erreurs de type ici.

94:23 Ici, si l'offre n'existe pas, parce qu'elle peut soit être nulle, soit être égale à ID, si elle n'existe pas, on devrait arrêter la validation ici, c'est-à-dire qu'on ne devrait pas continuer et on peut le faire en faisant un return false comme ceci dans le combat. Après chaque issue, rajoutez un return false ici aussi et ça répare nos problèmes de validation. Maintenant, on a juste à renvoyer le bon user ID et la méthode creat transaction se charge uniquement de créer une transaction dans Prisa. Et c'est Zod qui se charge de valider si l'offre est active, si elle n'appartient pas à l'utilisateur, et caetera. Maintenant, on va pouvoir tester ça tout de suite.

95:03 On va créer une nouvelle offre ici, on va l'appeler raclette. Venez manger une raclette chez moi. On va créer cette offre, on va l'activer. On se rend maintenant sur la page d'accueil. On va cliquer sur raclette et on va cliquer sur cette offre m'intéresse.

95:15 On clique dessus et là, vous ne pouvez pas acheter votre propre offre. Et c'est grâce à cette erreur de conforme qu'on peut se permettre d'afficher un beau message d'erreur à l'utilisateur et ne pas interrompre son flow. Il reste toujours sur la page de son offre et il sait exactement ce qu'il était en train de faire. Et c'est particulièrement bien s'il y a des inputs en plus ici, parce que ce qu'on aurait pu rajouter ici, c'est directement par exemple un avec le message qu'on aimerait laisser à l'utilisateur. Par exemple, votre message à cet endroit-là, d'auto, je viens pour dix-huit heures, si on clique dessus, le message persiste et c'est vraiment la beauté et la magie de conforme.

95:54 Ça veut dire que ça ne va pas réinitialiser notre formulaire et ça ne va pas faire crash notre application avec un gros message d'erreur qui prend tout l'écran. Maintenant, on va donc pouvoir supprimer ce champ a et on va pouvoir créer notre première transaction. Bien sûr, si on se connecte et qu'on est le propriétaire de cette offre, on va pouvoir conditionner ne pas afficher le bouton, ce qui nous éviterait clairement de cliquer sur le bouton, cette offre m'intéresse. Mais en fait, on rajoute des protections supplémentaires parce que, admettons qu'un hacker connaisse l'ID de l'offre il ne voit même pas cette vue côté client, il a envie d'appeler notre route et de faire un poste dessus, ça va l'empêcher d'exécuter toute cette logique parce que la validation va nous protéger s'il essaye d'acheter sa propre offre, par exemple, ou s'il n'est pas connecté ou s'il est propriétaire de cette offre. Si on veut rajouter cette logique supplémentaire, ici, on peut retourner dans le loader et à ce niveau-là, ici, on peut récupérer les informations comme le user ID à cet endroit-là.

96:52 Et côté client maintenant, on peut récupérer les informations de l'utilisateur connecté avec le hook use optionnel user qui nous renvoie les informations de l'utilisateur s'il est connecté. Donc, dans le cas où l'utilisateur est connecté, on va donc comparer son ID avec l'ID de l'offre ici, donc de l'offer et si l'ID du user est égal à l'ID du propriétaire de l'offre, alors dans ce cas-là, on va afficher nul, sinon on va rien afficher comme ceci. Là, on peut voir qu'on ne voit plus notre message, on ne voit plus notre formulaire parce que c'est l'offre de l'utilisateur. À la place, on pourrait même rajouter un spam ici vous consultez votre offre et on pourrait même pour encore plus d'UX, transformer ça en lien qui sera un lien vers la page my services slash offert point id avec comme message modifier mon offre et comme classe le bouton variant et on pourrait utiliser le variant blue hotline. Comme ceci, si je suis propriétaire de mon offre, je clique sur modifier mon offre et je me retrouve sur la page pour la mettre à jour.

97:56 Maintenant on peut voir que j'ai cliqué sur cette offre et malgré le fait qu'elle soit active et elle est active parce qu'on voit qu'elle est marquée comme active ici, si je clique sur cette page, la checkbox n'est pas cochée par défaut. Et ça, c'est un petit problème lié à Conform. Ici, dans notre input qui s'appelle checkbox fil, ce qu'on a envie de faire, c'est de rajouter ici un spread. Donc on va spread les props du bouton, mais on va changer la variable check. On peut voir ici que si on met check à trop et qu'on rafraîchit la page, ça ne va rien faire.

98:28 Par contre, si on met un Default check à trop, là ça va afficher le statut de cette checkbox parce que c'est une checkbox non contrôlée. Mais en fait, je viens de me rappeler que ici, on vient de mettre les default value pour le title, la description et le presse. Mais on a oublié de rajouter les default value sur l'active. Donc c'est une petite erreur d'inattention de ma part. Mais ici, si on essaye de rajouter la valeur à trop, normalement, ça devrait être bon.

98:57 Là, j'actualise la page et on peut voir que l'offre est bien false. Et si on rafraîchit cette page et qu'on la met à false, ici elle est marquée comme false et ici elle est marquée comme false. Ici, on va retourner sur la liste. Elle est active et ici, elle est de nouveau active. Maintenant qu'on a corrigé ce bug, on va tout de suite tester de créer une transaction.

99:15 On se rend donc sur la page d'accueil et on va consulter la liste des offres qui sont disponibles. Et j'aimerais quand même rajouter un petit indicateur ici pour savoir si cette offre m'appartient ou si je peux faire une transaction. Pour ce faire, on va retourner dans notre fichier index point TFX à cet endroit-là et nous avons le service card ici et nous avons besoin de récupérer une donnée supplémentaire qui sera l'ID de l'utilisateur. Et pour ce faire, on va devoir modifier la fonction awightd get of third. On se rend sur awightd get of third et à ce niveau-là, on va récupérer le user ID dans le select et maintenant nous allons récupérer cette information ici et nous allons appeler le hook qui s'appelle use user ou plutôt use optionnel user et nous allons sauvegarder cette valeur dans une variable.

100:03 Maintenant, on peut tout simplement faire un booléen is owner est égal à user point id triple égale user id. Et maintenant, on sera capable d'afficher si cette offre nous appartient ou pas. Donc ce qu'on peut faire, c'est qu'on peut rajouter un spam conditionnellement. Et si elle nous appartient, on va rajouter entre parenthèses, moi comme ceci et on va mettre le texte en tout petit à dix pixels et on va le mettre en gris. Et maintenant, nous allons modifier ici, on va mettre items center pour centrer sur la ligne comme ceci.

100:33 On peut voir que ce n'est pas très visible. En fait, il nous faudrait plutôt utiliser une icône. On va regarder ce qu'on pourrait utiliser comme icône. Par exemple, une icône user, on importe depuis le site. Et à la place, ce qu'on va faire, c'est qu'on va mettre un size de cinq et on va voir ce que ça donne.

100:47 Du coup, on va self close ce composant, donc on va le fermer directement comme ceci et ça m'a l'air d'être plutôt pas mal. On va l'afficher en vert comme ceci pour bien remarquer que c'est notre offre et on peut voir qu'on a donc les deux premières offres, mais que pour la raclette, cette offre ne nous appartient pas et on peut tout de suite annoncer à l'utilisateur que cette offre nous intéresse. Donc maintenant, on va cliquer sur ce bouton et ça nous a créé une nouvelle transaction et ça nous a amenés sur la route qu'on a créé tout à l'heure. Le fichier s'appelle transaction point dollars transaction ID comme ceci et ça ne renvoie rien actuellement. Donc ce qu'on va faire, c'est qu'on va exporter une fonction par défaut qu'on va appeler transaction et on va renvoyer pour le moment un.

101:31 On voit notre. Donc on va pouvoir continuer et maintenant on va pouvoir créer une méthode qu'on va appeler get transaction. Pour ce faire, vous connaissez maintenant la musique, on va dans le dossier serveur et nous allons créer un nouveau fichier qu'on va appeler transactions point serveur point t s. Et on va copier exactement les mêmes méthodes qu'ici, c'est-à-dire qu'on va lister les transactions. Donc je fais clairement un copier coller.

101:54 On veut une méthode get transactions, donc on va faire un fine many sur la transaction et nous allons récupérer pour le moment l'ID et le détail de l'utilisateur qui possède cette transaction et aussi le détail de l'utilisateur à qui on a fait l'offre. Donc on va faire une sélection du user et pour le moment, on récupère seulement le prénom. Ensuite, on va copier ce select et on va faire la même chose pour une transaction unique. Donc on va l'appeler ça get transaction. On renomme bien le paramètre ici, donc transaction ID et nous allons faire un find unique sur la transaction comme ceci.

102:32 Et c'est là qu'on colle notre select. Et maintenant la méthode create transaction qu'on avait créée initialement dans le fichier others point serveur et qu'on a dupliqué, on peut au final la supprimer du fichier others point serveur. Ce qui veut dire qu'on va devoir modifier notre import. Ici, on va retourner dans le fichier others point dollar others ID et à ce niveau-là, on importait la méthode create transaction depuis le fichier others point serveur. Et à la place, nous allons l'importer depuis le fichier transactions point serveur.

103:04 On importe create transaction comme ceci et maintenant on peut fermer tous nos fichiers et appeler notre méthode await get transaction qui va prendre en paramètre le contexte et le transaction ID. On fait donc la même chose que sur les autres pages. On récupère le paramètre transaction ID depuis params, comme ceci. On va faire un if, ça n'existe pas pour nous assurer d'avoir un ID qui n'est pas égal à undefined et on récupère le contexte pour utiliser Prisma. Et nous le renvoyons au format json en utilisant la méthode qu'on importe depuis remixran slash nod.

103:40 Maintenant, on peut récupérer cette donnée en utilisant le hook use loader data qui prend le type générique, et on peut voir qu'on récupère bien les informations de cette transaction. Mais on a oublié quelque chose. Effectivement, cette transaction ne peut être récupérée seulement pour l'utilisateur qui l'a demandé ou alors pour l'utilisateur qui est le propriétaire de l'offre. Donc déjà, la première protection que nous allons rajouter, c'est un await require user qui prend en paramètre un contexte et qui va déconnecter l'utilisateur s'il n'est pas connecté. Ensuite, on est sûr que l'utilisateur est connecté, mais on a besoin de passer son user ID comme paramètre ici.

104:21 Et son user ID, on le récupère comme valeur de la méthode require user ici. Donc, nous allons le passer en paramètres et nous allons retourner dans la méthode get transaction et nous allons rajouter un troisième paramètre User ID sous forme de string. Maintenant, avant de renvoyer cette transaction, nous allons d'abord la sélectionner comme ceci et nous allons ensuite récupérer donc select les valeurs ID pour les deux utilisateurs et on va mettre un premier check. Déjà, si la transaction n'a pas été trouvée, on met ce message d'erreur, la transaction n'a pas été prouvée. Ensuite, si on a une transaction et que le user ID est différent du user ID qui essaye d'accéder à cette ressource et que le user ID de l'offre est différent de l'utilisateur qui essaye d'accéder à cette ressource parce qu'il y a deux utilisateurs dans une transaction, le propriétaire de l'offre et le demandeur, alors dans ce cas-là, vous n'êtes pas autorisé à voir cette transaction.

105:15 Et maintenant, on peut enfin renvoyer la transaction pour la récupérer côté serveur. On se retrouve donc ici et on est sûr de pouvoir retrouver une transaction. Parce que si on ne la trouve pas, ça veut dire qu'il s'est passé une erreur. Encore une fois, on peut essayer ici de changer l'ID. Ici, on va retirer un caractère et on peut voir qu'on a cette erreur qui s'affiche.

105:35 Mais ce qu'on peut faire, c'est qu'on peut récupérer l'erreur de la page offert point offertd ici, qui sera à peu près similaire. On va donc la récupérer comme ceci et on peut renommer le texte, transaction introuvable, il semblerait que la transaction param n'existe pas. Maintenant, on va réimporter ici liste de mes annonces. Et bien sûr, le paramètre, ce n'est pas over ID, c'est transaction ID comme ceci. Maintenant, on peut voir que le message d'erreur est un peu plus classe et on va retourner en arrière pour récupérer les informations de notre transaction parce qu'on en a besoin.

106:10 Maintenant ce qu'on peut faire, c'est qu'on peut afficher toute cette donnée dans un pré avec un json point stringify de data et maintenant on peut voir qu'on a bien ces deux données. On a donc les informations de notre utilisateur et on a les informations de l'utilisateur et propriétaire de l'offre. C'est comme ça qu'on va pouvoir afficher les éléments sur cette interface. Donc, on va commencer à mettre en page cette page et on va s'inspirer du code qu'on avait fait dans la page my-service point dollar ofor idée. Ici, on retourne sur cette page et nous allons copier ce composant-là et nous allons le coller à cet endroit et on va récupérer toutes ces informations, notamment le formulaire, le titre de la transaction et en fait on va mettre le titre de l'offre qu'on n'a pas encore récupéré.

106:54 On va le récupérer tout de suite. Donc de cette offre, on va sélectionner le title, le price et la description. Pour le reste, on va commenter ça pour le moment et on a quand même envie d'importer un bouton ici parce qu'au final, cette transaction, ça sera une sorte de chat. Donc on va dire envoyer un message. Voilà, je me suis amusé à rajouter une petite interface ici avec les informations sur l'utilisateur, son avatar, s'il a été actif, nombre d'annonces et le nombre d'étoiles.

107:23 Bien sûr, tout ceci sont les données fictives qui ont été codées en dur et pour le visage, j'ai utilisé un site qui utilise du visage généré par l'intelligence artificielle. Ce qui nous intéresse nous, c'est de pouvoir créer une messagerie parce qu'on va vouloir échanger avec cet utilisateur après avoir créé la transaction. Et le chat, nous allons le créer ensemble. Nous allons donc créer un composant qu'on va appeler chatbox et qui va prendre comme props un tableau de messages. Et comme message, pour le moment, on va pouvoir écrire le type en dur comme ceci, il y aura une ID, un contenu, une date de création et bien sûr un user ID.

108:02 Le user ID étant l'ID de l'utilisateur qui a envoyé le message. Donc, on va pouvoir utiliser ce type pour le moment et nous allons renvoyer un div qui va utiliser Flex et qui va afficher tout simplement nos messages dans une sorte de box ici. Ensuite, on va donc boucler sur les messages. Je vais laisser Copilot me générer le texte pour le moment et nous allons tout de suite afficher ce composant avec quelques messages qu'on va générer et on va faire pareil, on va faire un arrêt from comme pour les étoiles pour générer les données de notre message. On va donc mettre une date, une ID et le user ID.

108:36 On va prendre comme user ID un, mais sur l'index, on va mettre qu'un message sur deux nous appartient. Donc on va prendre l'ID du user qui est connecté. Et pour ce faire, on va utiliser le hook use user parce que effectivement pour le moment on ne peut que utiliser le hook use optionnelle user sauf que il est possible de créer ce hook mais de renvoyer une erreur au cas où il n'y ait pas d'utilisateurs connectés. On est actuellement sur une route protégée donc l'utilisateur doit être forcément connecté. Donc ce qu'on va faire, c'est que je vais dupliquer ce hook, use optional user et je vais le renommer en use user.

109:11 Et tout simplement, au lieu de récupérer les informations depuis le rootloader data, il va appeler le hook use optional user comme ceci. Et si l'utilisateur n'existe pas, au lieu de renvoyer nul, on va à la place renvoyer une erreur, l'utilisateur n'est pas connecté. Et sinon, on va renvoyer les informations de cet utilisateur. Maintenant, on peut retourner dans le fichier et on peut importer le hook use user comme ceci et on peut voir que le user n'est plus égal à nul. Il est forcément défini.

109:39 Et maintenant, on peut retourner sur notre chat et on peut dire que si l'index est à la modulo de deux, donc en gros si l'index est pair, alors dans ce cas-là, ça va prendre notre user ID. Sauf qu'on ne va pas utiliser l'index. Ici, on va l'utiliser dans le map comme ceci. Pour ce faire, nous devons refactoriser un petit peu ce code en rajoutant les parenthèses. En fait, je fais une refactorisation parce que je me rends compte que je me suis totalement trompé sur l'utilisation de arrêt point from.

110:05 Donc maintenant, on peut voir que nos messages sont apparus. Bien sûr, c'est très moche et nous allons maintenant rajouter le style à cette messagerie. Ce qu'on a envie aussi pourquoi pas, c'est de rajouter l'avatar de l'utilisateur ici pour rappeler que c'est notre utilisateur. Donc effectivement, on va encore une fois rajouter des données fictives. On va mettre que si user ID est égal à un, alors on va dire que l'utilisateur s'appelle Virgile, sinon on va dire qu'il s'appelle Sébastien comme ceci, ça sera donc message point user ID et voilà.

110:35 Là, on a donc rajouté un baseis quatre-vingts pixels ici pour que les cartes de notre utilisateur aient la même taille, peu importe le nom. On a également rajouté un texte ellipseis et un over fluidn et un nombre de caractères maximum qui est égal à dix. Comme ça, si le nom dépasse comme ceci, on va mettre trois petits points. Ça ne va pas trop gêner notre interface. Et là, je suis en train de rajouter un div supplémentaire parce que ce qui ne me plaisait pas, c'est que les utilisateurs qui ont un nom plus grand ont du coup vu qu'on les centre au milieu, ils sont décalés à ce niveau-là du début.

111:10 J'aurais quand même préféré afficher l'image en premier, mais malheureusement, je pense que ça ne va pas être possible. Du moins, je ne vais pas prendre la peine d'essayer. Donc, on va retourner sur notre baseis quatre-vingts pixels comme ceci. Ça ça va nous suffire pour cet exemple et maintenant ma partie préférée, c'est d'inverser l'ordre des messages si c'est nous qui l'envoyons ou si c'est lui qui l'envoie. Et pour ce faire, ce qu'on va faire, c'est qu'on va recréer notre variable is owner et nous allons faire une comparaison du message en user ID avec le hook use user, on va réutiliser dans notre checkbox comme ceci et nous allons regarder si le user ID du message est égal à notre user edit comme ceci.

111:50 Sauf qu'on ne peut pas l'utiliser tout en haut ici parce qu'on est dans un tableau. Donc nous allons rajouter cette commission à ce niveau-là. Bien sûr, si c'est notre utilisateur, on va prendre le nom de notre utilisateur, sinon ça prendra le nom de l'utilisateur qui possède cette offre. Mais nous allons copier cette condition ici et ce qu'on a envie de faire à ce niveau-là, c'est dans le cas où nous sommes propriétaires de l'offre, alors on va faire un flex raw reverse, sinon on va faire un flex raw. Donc on va pouvoir supprimer ce flex pro et regarder à quoi ça ressemble.

112:21 Voilà, ça ressemble à ça. Ce n'est pas ouf, mais c'est un petit peu l'esprit que nous voulions faire. Maintenant, pour continuer, on va faire la même chose sur cette date ici pour que ce soit un peu plus joli. Donc on va transformer ça en classe conditionnelle et si l'utilisateur propriétaire du message est égal à mon utilisateur, alors dans ce cas-là, on va mettre le m l auto, sinon on ne va rien mettre. Donc dans le cas où c'est moi qui envoie le message, ça ressemble à ça et j'ai envie d'inverser ces conditions.

112:49 On va plutôt mettre que si ce n'est pas moi, alors on met la date en dessous du texte et maintenant, on a assez passé de temps sur le design. C'est quand même toujours aussi laid, mais ça, c'est parce que je ne me suis pas inspiré d'autres interfaces de chat et nous allons maintenant remonter ici et nous allons créer des vrais messages qui sont envoyés depuis la base de données. Pour ce faire, on va retourner dans notre schéma Prima. Ici, on va supprimer tous les autres fichiers et ce qu'on a envie de créer, c'est un nouveau modèle qu'on va appeler message. Et ce modèle va prendre une ID, une création, une mise à jour, un contenu et l'utilisateur qui envoie ce message.

113:24 Bien sûr, ce sera une relation sur l'utilisateur et le message aura une relation non pas sur l'offre, mais sur la transaction. On va renommer ces champs transaction edit et transaction sur le modèle transaction et ça prendra le nom de transaction edit. Maintenant, il nous suffit ici dans les transactions de rajouter un champ messager, mais en fait, il a été copié automatiquement par Prisma formate et maintenant, ce qu'on peut faire, c'est qu'on peut ouvrir le terminal et nous allons lancer une migration. On va donc faire un NPX, prisma migrate dev tiret tiret name metets. Js.

113:58 Et là, je me rends compte que je ne me suis pas déplacé dans le fichier back-end. On va d'abord faire un cd back-end et on va relancer cette commande. Et voilà, nous venons maintenant de créer cette migration qui s'appelle metets. Js. On peut maintenant fermer ce terminal et on va implémenter la logique dans ce même fichier ici dans les transactions point dollars transaction ID.

114:20 Pour ce faire, ce qu'on va faire, c'est qu'on va copier cette méthode, on va la renommer en action et ça sera une action function args et en premier lieu on va faire la même chose, on va vérifier que l'utilisateur est le bon, que la transaction existe et on va même faire ce get sur la transaction pour ne pas avoir à recopier cette logique-là. Une fois cette étape passée, on peut être sûr qu'il existe une transaction et qu'on en est le propriétaire. Donc, on va sauvegarder ce résultat dans une variable comme ceci et on va maintenant cliquer là et on va dupliquer cette méthode et on va l'appeler sand message qui va donc prendre une transaction ID, le contexte et le user ID. Bien sûr, ça va aussi prendre un content. Il sera sous forme de string pour le moment et nous allons faire exactement les mêmes vérifications ici.

115:09 Donc on va déplacer la logique à ce niveau-là. On appellera sendMessage plus tard et on va d'abord récupérer la transaction avec la méthode qui est définie dans le même fichier get transaction. Il prend donc le contexte, l'ID et l'utilisateur et nous allons maintenant créer un nouveau message en faisant un await du contexte et de prise var sur message. Et on va le create et nous allons le lier à la transaction en faisant un connect sur son ID. Là, pour le moment, Descript ne m'aide pas du tout.

115:39 Alors, je ne sais pas si je vais devoir lancer la commande NPX prizma generate dans le fichier backend pour pouvoir régénérer les types. Ça va peut-être nous aider. Maintenant, on peut voir que ça a quand même changé quelque chose et nous allons continuer en rajoutant le contenu et en se faisant une connexion sur l'utilisateur. Normalement, ça devrait être bon comme ça. On peut donc laisser cette méthode, on va renvoyer le message et cette méthode ne va rien renvoyer.

116:04 Bien sûr, la connexion se fera sur l'ID de la transaction, on va récupérer comme ceci. Maintenant que c'est fait, on peut retourner dans notre fichier et au lieu de faire get transaction, on va faire un said message et on va rajouter un contenu par exemple salut, je suis intéressé par votre offre et maintenant, on va tester cette implémentation. Mais pour ce faire, on a besoin d'avoir un formulaire. On va récupérer le bouton envoyer un message et on va copier ce formulaire dans le composant de chatbox et c'est à cet endroit qu'on va le coller. On le colle donc comme ceci et nous allons décommenter un des champs qui sera donc un field et qui va se charger d'envoyer le contenu.

116:42 Pour ce faire, on peut maintenant créer un schéma qu'on va appeler message skima et on va importer zone et on va rajouter un content de type string qui aura comme message requiere d'erreur le contenu du message est requis. Ici, la seule chose qu'on a besoin de faire parce que les autres vérifications sont déjà faites côté serveur. Maintenant, on peut utiliser le hook use form et on va répéter les opérations qu'on a déjà faites. La contrainte, c'est donc un get the de notre schéma zod. Le on validate va donc prendre un form data et ça va renvoyer un de notre form data avec comme schéma le message skimmer.

117:20 Et maintenant, on peut récupérer les informations des champs et de notre forme ici et on va pouvoir les en utilisant la méthode getform props et on va pouvoir envoyer tous nos props en utilisant la méthode get input props avec comme field le contenu et on va l'appeler message plutôt nouveau message au final d'un point de vue css ce serait quand même plus cool d'avoir un texte area field. Donc, on va remplacer ça par un texte area prop et un get text area props comme ceci, histoire d'avoir une mise en forme un peu plus stylé, même si dans notre composant pour le moment, on ne va pas renvoyer du markdown, on va renvoyer un string. Maintenant, on a envie d'utiliser un fetshare à la place. Donc, on va faire un fetshare est égal à use fetshare pour ne pas recharger la navigation à chaque fois. Donc, on va remplacer notre forme par un fetshare point forme.

118:11 Ici, on ne va pas reload le document et maintenant, on va se charger de la validation côté serveur. Donc, on va prendre notre schéma zone et on va le déplacer tout en haut ici et on va maintenant faire ce qu'on a déjà fait à ce niveau-là juste avant de créer notre offre. Va donc récupérer la forme data avec un await de request point forme data bien sûr il va falloir qu'on importe la request et on va fixe cette typo et enfin on va utiliser une submission avec un par swissod de notre forme data sur le schéma message schéma. Et bien sûr, pour le reste du code, on peut s'inspirer du code qu'on avait écrit dans le offert edit ici dans notre action, c'est-à-dire toute cette logique si avec des essais on n'a pas réussi, alors dans ce cas-là, on renvoie le message d'erreur. On va donc faire ceci.

118:57 Sinon si ça a réussi alors on va renvoyer du j zan on va pas renvoyer le statut cassant et on va reset le formulaire avec un reset forme à trous et on va tester voir ce que ça donne bien sûr on va désactiver le bouton on est en train de charger, donc d'envoyer un message avec un fetcher point state est égal à submettais comme ceci. Et maintenant, on va initialiser les valeurs de notre formulaire. Ici donc, ce fetcher va le déplacer juste avant le hook use form et on va rajouter la clé last result qui sera égal à setcher point data point result. Et maintenant on va faire un test. On va mettre le Default value à un message en particulier par exemple Hello et là on peut voir après avoir actualisé la page que ça affiche bien notre default value.

119:43 Donc conforme et bien connecté, on va tester tout de suite. Maintenant on va envoyer un message salut ça va et là on se rend compte qu'on a une erreur et c'est revenu. On va ouvrir notre terminal et on se rend compte ici qu'on a un problème avec la propriété create parce qu'on a fait un create sur une valeur qui n'existe pas. Alors à quoi est-ce Est-ce que veut dire que notre message ici n'a pas été détecté par l'application On va qu'il le terminal et on va refaire un NPM wend dev pour en être sûr. Maintenant on va actualiser cette page et on va essayer de renvoyer un message test Là, on peut voir qu'on n'a plus eu cette erreur.

120:18 On va renvoyer un message test deux. Là, on n'a plus d'erreur et ça réinitialise le formulaire, ce qui veut dire que la validation a été effectuée. Donc maintenant, ce qu'on va faire ici dans le loader, c'est qu'on va rajouter les vrais messages. Ici, on va sélectionner les messages et on va sélectionner le user ID et le contenu, la date de création et bien sûr l'ID comme ceci. Et maintenant, ici, on peut retourner dans notre composant et au lieu de renvoyer tout ça, on va renvoyer à la place les vrais messages de cette conversation.

120:50 Donc data point message. Et là, on peut voir qu'on a vraiment les bonnes informations, donc les messages que j'ai envoyés. Et ici, ça écrit salut, je suis intéressé par votre offre parce qu'on a oublié dans notre action d'utiliser la valeur de notre funmission point value point content. Maintenant, ce qu'on a envie de faire, c'est de changer le type ici parce que au lieu de prendre le type de message, ça va prendre le type que nous renvoie la méthode get transaction. Pour ce faire, on va mettre aussi un awated return type, payof get transaction.

121:22 Et après, on va ouvrir les crochets pour dire qu'on va prendre un message et on va prendre un message unique. C'est exactement le même type, mais au moins cette fois, on hérite du type de notre méthode. Sauf qu'on a une petite erreur de validation ici parce qu'il s'attend à recevoir une date, sauf que à cet endroit-là, nous, nous avons pas une date, nous avons un string. Et c'est parce qu'on a oublié le serialize from, on va importer ici, comme ceci. Maintenant, on peut voir sur le type qu'il s'attend à recevoir un string.

121:50 Effectivement, côté serveur, notre méthode return json va sérialiser au format json la date et ça va donc la transformer en string. Mais ce qu'on aimerait maintenant, c'est comme vous voyez, on affiche les avatars et le nom de l'utilisateur. Donc qu'est-ce qui se passe dans le cas où l'utilisateur ça ne soit pas nous Eh bien, on va récupérer cette information. Il y aurait deux manières de le faire. Soit on passe en propre l'utilisateur de l'offre auquel on est sûr que ce n'est pas nous.

122:16 Dans ce cas-là, ici, au niveau de cette ternaire, si ce n'est pas nous, alors c'est le offert en user en main comme ceci. Mais admettons qu'il y a un bug dans l'application et qu'il y ait plusieurs utilisateurs. Alors admettons que ça ne soit pas vraiment le nom du propriétaire de l'offre. Si on voulait vraiment être très précis, il faudrait à tout prix qu'on affiche ici les informations de l'utilisateur. Donc, c'est ce qu'on va faire.

122:39 Nous allons récupérer le nom du vrai utilisateur du message ici, ce qui va nous permettre dans message, au lieu de faire une comparaison sur le user ID, de récupérer le name qui sera donc le vrai name cette fois. Maintenant on va retester, on va envoyer un message, on peut voir que ça fonctionne très bien. Et si on se déconnecte maintenant et qu'on se connectait avec l'autre compte, j'ai réussi à récupérer l'ID de la transaction en faisant page précédente et là je peux maintenant me charger de renvoyer une réponse. Mon annonce est très cool et voilà. Là on peut voir que ça fonctionne très bien.

123:11 Ça affiche mon nom sur la droite et ça affiche le nom de l'utilisateur qui m'envoie le message sur la gauche. Bien sûr, les données sur la gauche sont toujours fictives, la date d'activité, les annonces actives et le nombre d'étoiles. Mais cette application commence à ressembler à quelque chose. Alors maintenant, la question qu'on pourrait se poser, c'est qu'est-ce qui se passe s'il y a une trentaine de messages. Ici par exemple, on va spammer les messages, on va l'envoyer plein et là on se rend compte que si on actualise la page ici, on n'a pas le chat avant d'avoir envoyé au moins quinze messages.

123:41 On n'a pas envie de scroller vers tous ces messages, donc on va tout de suite limiter ces messages en mettant ici dans le div une hauteur maximum. On va mettre pour le moment trois cents pixels, on va même mettre deux cents pixels juste pour le dev, on va overflow idem et maintenant on peut voir qu'on a donc seulement trois messages et en fait on ne va pas mettre overflow idem, on va plutôt mettre overflow y scroll comme ceci et maintenant on a une barre de scroll qui va afficher les derniers messages comme ceci. Sauf que ce qu'on aimerait nous, c'est d'être scrollé vraiment au dernier message qui a été envoyé ici parce que là, on voit les tout premiers messages de l'offre et si on a une offre avec plus de cent messages on a envie d'arriver tout à la fin ici et pour ce faire on va devoir utiliser ref ici qui est un propre de react donc on va utiliser ce hook on va faire un use ref ça sera un html div element va être égal à nul et on va appeler cette variable div ref comme ceci et maintenant on va utiliser un use effect qui va changer à chaque fois que les messages changent comme ceci et à chaque changement s'il existe un diffref current, donc là je fais un early return pour ne pas exécuter la méthode s'il n'existe pas le diffref current.

124:50 Et s'il en existe un, alors on va faire un scroll top est égal à diffref current scrollate. Je ne sais pas, c'est chat GPT qui me suggère, on va tester cette implémentation. Là, on va actualiser la page et ça a l'air de bien marcher. Mais maintenant la question, c'est est-ce que ça va nous scroll lorsqu'on envoie un nouveau message Test test, ça a l'air de nous avoir scroll. On va refaire un essai, on va scroller au milieu, on va envoyer un message, hello.

125:12 Et là, ça a l'air de nous avoir scroller tout en bas. Donc, ça fonctionne plutôt bien, c'est vraiment cool. On va conserver ce morceau de code comme ça, s'il y a des centaines et des centaines de messages, ça ne va pas polluer notre interface. Maintenant qu'on a implémenté notre chat, on va retourner tout en haut dans notre application ici et nous allons rajouter dans le menu un écran pour suivre notre liste de transactions pour pouvoir les retrouver. On va donc retourner dans le fichier root ici et on va cliquer sur la navbar à cet endroit-là et nous allons rajouter un lien qu'on va appeler transaction et ça va rediriger sur la page transactions.

125:49 Bien sûr, cette page est seulement disponible pour les utilisateurs connectés. Et au final, si on retourne tout en bas, on voit qu'on a une page qui s'appelle demande. Donc, on va renommer notre lien en demande comme ceci et nous allons le mettre en text x s comme ceci. Et on va même faire la même chose pour les autres liens et le bouton se déconnecter. Maintenant, on peut voir qu'on a un petit problème au niveau des icônes qui sont assez grosses, mais on ne va pas régler ce souci.

126:14 Maintenant, on a juste envie maintenant de créer notre page transaction et ce fichier, on l'avait déjà créé. C'est le fichier qui s'appelle transaction point index point f x ici. Et ce qu'il nous reste à faire maintenant, c'est de lister les transactions actives pour cet utilisateur. Pour ce faire, on va s'inspirer de la page my-services point index. Nous allons copier ce composant là comme ceci.

126:37 Nous allons même copier la card en dessous et maintenant on va se rendre dans ce fichier et nous allons coller tout ça. Et on va bien sûr renommer notre page. On va l'appeler my transactions comme ceci. Et bien sûr, on pourra récupérer depuis le serveur une liste de transactions. Pour ce faire, on va donc retourner dans le fichier transactions point serveur point t s ici et nous avions cette liste qui s'appelle get transactions qu'on a pour le moment utilisé nulle part.

127:04 On va donc à cet endroit-là récupérer quelques informations sur l'offre est demandée dans le select. À cet endroit, on a envie quand même de connaître le titre et le prix. Et nous allons aussi récupérer la date, le created at de cette transaction et pourquoi pas le update at de cette transaction pour savoir à quand remonte le dernier message qui a été envoyé et à quand remonte la date de création de cette transaction. Donc, on va pouvoir appeler cette fonction maintenant dans notre méthode Loader à cet endroit-là. D'abord, nous allons faire un await require user pour dire qu'on oblige la connexion sur cette route.

127:39 Donc, si un utilisateur est déconnecté, il sera redirigé sur la page d'accueil et ensuite nous appelons le get transactions en passant notre contexte. Mais on a aussi besoin de passer l'ID de l'utilisateur mais on a aussi besoin de passer l'ID de l'utilisateur connecté. On va donc le récupérer une nouvelle fois comme ceci. On va dire que user ID est égal à user point ID. On n'oublie pas de le mettre en kamel case et nous allons maintenant await le get transactions et réparer notre à cet endroit-là et nous allons sauvegarder dans une variable nos transactions avant de les renvoyer au format JSAM côté client comme ceci, sous forme d'objet, comme ça on peut déstructurer après cet objet côté client.

128:18 Maintenant, le morceau de logique qu'on a rajouté à cet endroit, on a besoin de rajouter un paramètre qu'on va appeler user ID ici et c'est là qu'on va avoir une petite difficulté parce qu'il y a deux types d'utilisateurs. Il y a l'utilisateur qui a créé la transaction, donc l'utilisateur qui souhaite posséder le service et il y a l'utilisateur qui possède l'offre. Et nous avons besoin de récupérer les transactions qu'on a demandées et les transactions qu'on nous a demandées. On aurait plusieurs manières de le faire, mais je vais préférer la solution simple. On va récupérer en premier une liste des transactions que nous avons demandées nous-mêmes, donc my requestd transaction par exemple et nous allons ajouter notre connexion ici avec au-dessus du select un ware et nous voulons récupérer seulement les transactions où le user ID est égal au user point ID.

129:09 Et si on avait voulu sur la même liste récupérer les informations soit de l'utilisateur qui a cet ID, soit le propriétaire de l'offre, ça aurait été possible en faisant un or comme ceci. On aurait fait un or, on aurait mis soit le propriétaire de la transaction est récupéré ou alors le propriétaire de l'utilisateur qui est dans l'offre, sur le user ID comme ceci, est récupéré. Et si on exécute ce code-là, en une seule requête, on va récupérer les informations de l'utilisateur. Donc les transactions qu'il a effectuées lui, les transactions qu'on lui a demandées. Ce que j'aurais préféré, c'est pouvoir séparer les deux, mais au final, ce n'est pas plus mal de faire comme ça.

129:47 Donc là, on a notre condition avec ce soit on récupère les transactions qui ont été créées par l'utilisateur, donc par nous, soit les transactions qui ont été faites à cet utilisateur et on va donc maintenant renvoyer cet objet transaction ou alors on peut aussi faire un filtre comme ceci en faisant my requestd transaction est égal à transaction et on fait donc un filtre, un filter sur les transactions où nous possédons un user point ID. Ça veut dire, ça, c'est la transaction que l'on a demandée nous. Pour ce faire, on a besoin de récupérer également l'ID de l'utilisateur comme ceci. Et ensuite, en deuxième paramètre, on peut effectivement faire ce que Copilot nous suggère, my oford transaction et là, on fait le filtre sur le user ID de notre offre à ce niveau-là. Et dans ce cas-là, on récupère les transactions qui nous ont été demandées.

130:38 Mais pour ce faire, on a également besoin de sélectionner Lady comme ceci. Et maintenant, on peut faire un return de ces deux méthodes au format d'objet comme ça et côté serveur, maintenant on va pouvoir déstructurer nos deux transactions. On a donc Myoford, transactions et My requestd, transactions. Et on va pouvoir les afficher et les récupérer côté client comme ceci. Donc en premier, on peut rajouter comme titre mes demandes.

131:03 Mes demandes va contenir la liste des transactions que nous avons demandées nous, donc My requestd transaction, comme ceci. Et pour ce faire, nous allons recréer cette card qu'on va maintenant nommer transaction card et nous allons changer le type, on va dire que c'est un serialis from de get transactions et ça va prendre comme enfant la clé my heriquest transaction par exemple et nous allons mettre que donc on récupère une des transactions du tableau et on va enfin renommer composant-là. Maintenant, on doit aussi réimporter le composant link de React et nous allons rediriger sur la page transactions slash transaction point ID comme ceci et nous allons récupérer les informations sur notre offre avec le offert point title et le offert point price en réimportant bien sûr toutes les méthodes. Et nous avons même la date de la transaction avec un transaction point updated at mise à jour le transaction point updated comme ceci et on peut supprimer le champ description et certains des autres champs comme celui-ci. Maintenant du coup on va boucler sur chacune des transactions et on va renommer le propre ici et on peut voir si on actualise la page qu'il n'y a aucune demande qui nous a été faite.

132:16 Effectivement, parce qu'on possède bien une transaction, mais c'est l'utilisateur qui nous a demandé notre service. Donc, on va tout simplement dupliquer cette ligne-là, ce composant comme ceci. On va mettre tout ça dans un fragment et cette fois, on va appeler ça mes offres et on va boucler sur le deuxième objet du tableau my offertd transactions comme ceci et on peut voir qu'on a bien une offre qui nous a été faite pour la raclette. Maintenant, on va peut-être changer un petit peu le design. Voilà ce qu'on va faire.

132:45 Maintenant, on va rajouter un div avec un flex call et un gap de cinq par exemple, quand même montrer une limite entre les deux et maintenant on peut passer à la suite. Donc on va cliquer sur cette offre-là, raclette à dix euros et ce qu'on a envie en tant que propriétaire de cette offre, c'est de pouvoir accepter ou refuser la demande de la personne qui est intéressée. Mais pour pouvoir refuser ou accepter sa demande, il faut d'abord qu'il nous fasse une demande. Donc ce qu'on va faire, c'est qu'on va se déconnecter ici, on va se reconnecter en tant que propriétaire de cette annonce et on va déjà accéder à mes demandes pour voir que cette fois, on retrouve bien la transaction dans mes demandes. Ici, on va donc pouvoir cliquer dessus et au lieu d'envoyer un message, on aimerait rajouter un autre bouton pour pouvoir faire une offre.

133:32 Donc, on peut retourner sur le détail d'une transaction, sur le fichier transaction point dollar transaction ID comme ceci. Et à ce niveau là, on a donc notre formulaire et ce qu'on peut faire, c'est qu'on peut le dupliquer comme ceci. Maintenant, on a donc deux formulaires au même endroit. On peut les voir ici et c'est vrai que ça me pose un petit problème parce que j'aurais préféré que les deux boutons soient côte à côte comme ceci, donc dans un div qui utilise flex, je pense que ça aurait été plus stylé d'avoir le bouton de faire une offre à ce niveau-là. Le souci, c'est que ce bouton-là, il est à l'intérieur d'un form, ici donc d'un formulaire.

134:06 Il est à l'intérieur du formulaire d'envoi de messages. Là, je viens de retirer le l m auto des deux boutons et je l'ai rajouté sur son parent et on va voir ce qu'on peut faire. Effectivement, je pense qu'on va quand même réussir à avoir le comportement que nous voulons. Et pour ce faire, nous allons donc dupliquer ce fait cher. Et pour que dans le futur, ce code soit plus maintenable, on va quand même le renommer.

134:27 On va donc l'appeler send message fait cher comme ceci parce que c'est le formulaire qui nous permet d'envoyer un message classique. Maintenant, on va donc le dupliquer et le deuxième faithare va s'appeler send offert faithare comme ceci et il va hériter des mêmes méthodes ici. Sauf qu'on va dupliquer ce formulaire-là et on va le renommer en offert forme et offert fields comme ceci et cette fois ça prendra un autre schéma. Et ce schéma, ça sera donc un offert schéma, il sera pareil, un objet avec une propriété price qui sera donc un number. Et bien sûr, on peut mettre un prix minimum.

135:05 Le prix doit être supérieur à zéro, donc on va mettre minimum un, comme ça, on est bon. Maintenant, on va donc récupérer ce schéma dans notre composant à cet endroit-là, dans notre deuxième use form comme ceci, et c'est bien sûr le Send Offer Fetcher qui va alimenter le Last result. Mais ça ne nous aide pas beaucoup parce qu'il faut qu'on puisse placer à cet endroit-là notre formulaire avec un bouton à l'intérieur. Alors comment est-ce qu'on pourrait faire Bien ici, à l'intérieur, on va rajouter un formulaire. Donc on va englober notre bouton par un formulaire à l'intérieur de notre premier formulaire et on va le nommer Sendother Patch comme ceci et on va cette fois récupérer les props, pas de forme, mais de offert form comme ceci.

135:49 Sauf que ce bouton, il va prendre un argument supplémentaire. Il va prendre un forme ici et le form, ça va être l'ID de notre formulaire. Ici, donc on va dire que le form, ça va être get form props offert form point id comme ceci. Maintenant, on peut même retirer le style de ce composant, à part le flex et un petit gap, parce qu'à l'intérieur, nous allons rajouter un field et ça prendra comme input notre prix, le prix que l'utilisateur souhaite proposer à l'utilisateur. On va donc lui appeler la méthode get input props sur le offert fields point price comme ceci.

136:25 Et en deuxième paramètre, on va mettre que le type, c'est un number et on va déstructurer ça. Ensuite, nous allons ajouter un label. Il y aura comme valeur offre ou plutôt votre proposition en euros. On va plutôt mettre votre offre en euros comme ça et bien sûr les erreurs en cas d'erreur. Je me rends compte qu'en fait, à ce niveau-là, le bouton, c'est beaucoup plus stylé de l'avoir comme ça, mais l'input pour offrir le prix, c'est un peu moche.

136:50 Donc ce qu'on aurait pu faire à la place, c'est peut-être en cliquant sur faire une offre, ça ouvre un modal où on puisse sélectionner le prix de notre offre, mais on va pas prendre le temps de faire comme ça aujourd'hui. En fait, on va tout simplement déplacer au final ce composant en dessous, comme ça on aura aucun problème à se faire. On aura donc nos deux formulaires comme ça, un formulaire pour faire une offre qui aura comme valeur green outline comme ceci et on va donc rajouter notre m l auto et on va mettre un h r en dessous et maintenant si on clique sur le bouton pour faire une offre, ça va nous afficher cette erreur ici le prix est requis. Si on met zéro, par exemple, ça va nous afficher l'erreur. Le prix doit être supérieur à zéro.

137:31 Maintenant, qu'est-ce qu'on a envie de faire au cas où l'utilisateur fasse une offre Parce que là, pour le moment, on a envoyé des messages ici et notre offre, on aimerait s'inspirer du bon coin pour faire une offre. Alors la question qu'on pourrait se poser c'est est-ce qu'on ajoute une propriété dans le message par exemple un price qu'on puisse du coup accepter ou refuser ou alors est-ce qu'on crée un nouveau modèle qu'on va appeler offert qui contienne les informations de cette offre spécifique Personnellement, je pense que ça serait plus simple de rajouter à cet endroit dans un message un prix qui serait sous forme de flotte et qui serait bien sûr optionnel parce que tous les messages ne possèdent pas de prix. Maintenant, ce qu'on peut faire, c'est qu'on peut aussi rajouter un statut. Par exemple, est-ce que l'offre a été acceptée Est-ce que l'offre a été refusée Ou est-ce que l'offre est en attente Donc, soit on peut utiliser un énum, on peut utiliser un int et gérer cet inum dans notre application. Ce qu'on va faire, c'est qu'on va dire que ça va être un avec comme valeur par défaut zéro et on va noter les différents statuts ici.

138:31 Zéro, c'est donc un message classique sans offre. Dix, ça va être une offre en attente. Vingt, ça va être l'offre acceptée et quatre-vingt-dix, ça va être l'offre refusée. On va donc copier ce commentaire à cet endroit-là et nous allons maintenant retourner dans notre fichier transactions. Serveur et ici nous allons déclarer un énum qu'on va appeler transaction message status et qui aura plusieurs valeurs.

138:56 Alors pour les énums, vous n'êtes pas obligé de mettre le égal, d'ailleurs on doit le retirer et on va rajouter chacun de nos statuts. Message est égal à zéro, pending offert est égal à dix, accepterd offert est égal à vingt et rejectd offert est égal à quatre-vingt-dix. Bien sûr, cette ligne, on va l'exporter parce qu'on va l'utiliser à cet endroit-là. Par défaut, chacun des messages sera considéré comme un message classique, mais ici, nous allons nous inspirer de la méthode send message pour la dupliquer et en faire une méthode send offert comme ceci. Et ça va prendre notre schéma que nous avons déclaré à cet endroit-là, qui s'appelle offert skimmer.

139:35 Et on peut l'utiliser, on va exporter ce schéma et on peut l'utiliser comme props à cet endroit-là, parce qu'ici, on peut voir qu'on avait rajouté le contenu sous forme de string, mais cette fois, on va faire autre chose. On va donc importer notre schéma et on va utiliser le type zod. Infer type of offerskima et on n'oublie pas d'importer ce schéma-là. Et nous allons renommer ce champ en offer data, comme ceci. Et cette fois, au lieu d'avoir un contenu, on va rajouter le prix et le statut que nous avons défini ici dans notre modèle.

140:05 Mais d'abord, vu qu'on a modifié notre schéma avec Prizma, on doit ouvrir un terminal. On va faire un CD dans Backend et on va faire un NPX prisma migrete dev tirer name pour créer une nouvelle migration qu'on va appeler add offer in message comme ceci. Voilà maintenant que c'est fait, on peut faire NPX prisma générique pour régénérer les types avec type script. Maintenant que c'est fait, on retourne dans notre fichier transaction point serveur et à ce niveau-là, on va rajouter donc un price, il sera égal à offert data point price et le statut par défaut est égal à transaction message status point landing offert. Et il nous faut cependant un contenu.

140:44 Mais le contenu qu'on va afficher va être écrit en dur, ça va être les informations de l'utilisateur qui a demandé la transaction, ça va être transaction point user point m, vous avez fait une offre de offert data point price, comme ceci, aussi simple que ça. Maintenant, on peut retourner dans notre application à ce niveau-là et nous allons utiliser ce schéma côté serveur. Maintenant, on retourne côté serveur dans notre action ici et on peut voir qu'on récupère donc le form data et on fait un parse sur notre schéma de messages. Sauf qu'on n'est pas sûr que notre schéma contienne un message parce que soit ça contient un contenu, soit ça contient un prix, mais ça ne peut pas contenir les deux. Alors comment va-t-on faire dans ce cas-là Eh bien, nous avons combiné nos deux schémas ZOD parce qu'il est possible entre guillemets de les combiner pour savoir laquelle de ces deux actions a été effectuée.

141:36 Et pour ce faire, on va rajouter une propriété qui est commune à nos deux schémas et ça va être une propriété qu'on va par exemple appeler action. Ici, on va mettre par exemple un littéral qui prendra comme valeur offert. Et nous allons ajouter cette même propriété dans le deuxième schéma, mais cette fois, il prendra comme valeur message comme ceci. Maintenant, ce qu'on peut faire, c'est qu'on peut créer un troisième schéma d'ol qu'on va appeler offert message schéma et qui va utiliser Copilot new used geste une union comme ceci. Maintenant, c'est cette union-là que nous allons parse à ce niveau-là.

142:10 Et maintenant, ici, on peut rajouter un switch sur la propriété action qui est contenue dans submission point value point action. Et on peut mettre que dans le cas, dans le case où c'est un message, alors dans ce cas-là, on va exécuter ce code-là. On va ouvrir les crochets, on va exécuter ce code-là. Et là, on peut voir que la propriété content est bien définie. Par contre, on ne retrouve pas la propriété Price.

142:36 Par contre, ici, on va rajouter le deuxième cas. Dans le cas où ce n'est pas un content, mais une offre, alors ici, dans ce cas-là, on peut voir que TypeScript a bien compris ce qu'on essaie de faire. Dans ce cas-là, ça contient pour prêter qui s'appelle Price et on va faire un send offer comme ceci, avec comme valeur le offer data et le submission point value sera sa valeur. Et bien sûr, en cas de problème, on renvoie la submission et on reset le formulaire. Et dans le cas où ça ne soit ni cette action ni celle-là, ce qu'on va faire, c'est qu'on va rajouter une propriété Default qui va donc renvoyer une erreur côté client.

143:10 Soit on peut renvoyer du j comme ceci, soit on peut même aller jusqu'à cette réponse pour déclencher notre erreur. Et maintenant, on va faire un essai. Ici, on va envoyer un nouveau message, mais il y aura une erreur parce que on n'a pas envoyé l'action qui est attendue dans l'Ochimazone. Si on envoie un message comme ceci, hello, qu'on clique sur envoyer, pour voir que le formulaire ne s'envoie même pas côté serveur, parce que la propriété action n'existe pas. Donc on n'a même pas pu tester ce message erreur en fait, Conform nous a trop bien protégés.

143:43 Donc ce qu'on va faire, c'est qu'on va rajouter ici à notre bouton un nom qui aura comme nom donc action et comme valeur, on aura message. Maintenant, si on envoie un message, salut, ça va On clique sur envoyer, on peut voir que le message part bien. Et ce qu'on peut faire maintenant pour la deuxième action, c'est de rajouter le nom action et comme valeur, on va mettre offer comme ceci. Et si on envoie une offre, par exemple vingt euros, on clique sur faire une offre. Là, on peut voir qu'on a l'erreur boundry qui vient d'apparaître.

144:10 Ça veut dire qu'il y a eu une erreur quelque part. Donc, on va ouvrir notre terminal ici et on peut voir qu'on a une erreur au niveau du prix ici. Le prix n'est pas disponible parce qu'on l'a fourni en fait au format number, alors que c'était en float, je crois bien. Donc, on va débugger ensemble, on va retourner dans ce fichier et on va faire un console log de offert data comme ceci et on va refaire une offre ici à vingt euros. On clique sur faire une offre et si on ouvre notre terminal, ici on peut voir qu'on a bien vingt-cinq euros comme prix et une action qui s'appelle offert, mais on a toujours cette erreur sur le price parce qu'il s'attend à recevoir un float.

144:47 Donc, on va créer une nouvelle variable qu'on va appeler price as float et on va appeler la méthode parce float qui va prendre comme paramètre un string. Donc, on va faire un to fix ici avec deux décimales et nous allons appeler par float comme ceci. Il y a austin qui me suggère d'utiliser number point par float, donc on va l'utiliser et maintenant, on va l'utiliser aux deux endroits ici et on va même le console loguer et maintenant on actualise notre page et on va refaire une offre une dernière fois, on ouvre le terminal, on soumet notre offre on peut voir qu'on a toujours cette erreur. Alors cette erreur est vraiment étrange parce que ça voudrait dire potentiellement que notre base de données n'a pas été mise à jour. Donc ce qu'on peut faire, c'est qu'on peut ouvrir un deuxième terminal, faire un cd dans backend, on peut faire un NPX prisma migrate deploy pour vraiment pousser chacune des migrations en prod et là on peut voir qu'on a le message no pending migration to apply, ce qui signifie qu'il n'y a aucune migration en attente.

145:43 Je ne comprends pas d'où ça vient. Donc ce que je vais faire, c'est que je vais clairement définir en dur la valeur de price à flot et je vais mettre, je vais mettre par exemple douze point trente-quatre parce qu'on est sûr que ça sera du coup un float vu qu'il y a le point virgule ici et on va refaire une offre, n'importe laquelle. De toute manière, ça va utiliser notre valeur qui est codée en dehors. Et si on fait une offre, cette fois, on a encore une fois cette erreur assez étrange de la part de Prizma qui nous souligne bien la valeur Price ici en disant que ce paramètre n'existe pas. Pourtant, on l'a bien rajouté sur le modèle message ici et là nous sommes en train de créer un message.

146:20 Ok donc là, j'ai fait quelque chose qui nous a débloqué, on peut voir ici que l'offre a bien été créée et en fait j'ai tout simplement pris mon terminal. J'ai fait un contrôle C et j'ai relancé la commande NPM rundev qui a donc re build le call source de l'application. Alors c'est sûrement pour ça qu'il y avait un petit problème au niveau du Hot triloding. Maintenant, on peut du coup décommenter cette syntaxe et nous allons envoyer une deuxième proposition. Cette fois, ça va être à dix euros vingt-quatre comme ceci.

146:46 On va cliquer sur faire une offre et là, on peut voir que la proposition est bien effectuée. Donc, ça marche plutôt bien. Maintenant qu'on a fait une proposition, on a envie ici d'avoir un bouton côté propriétaire de l'offre pour pouvoir accepter ou refuser la dernière offre qui a été faite. Donc on va encore une fois nous déconnecter et nous reconnecter en tant que propriétaire de cette offre comme ceci et on va retourner sur notre demande ici et là on peut retrouver les deux propositions qui ont été faites. Et on aimerait rajouter le bouton soit en dessous ici, mais on pourrait aussi le rajouter ici juste à côté de la date.

147:23 Et pour ce faire, on va retourner dans notre chatbox ici, sur notre tableau de messages parce qu'on a besoin maintenant de récupérer des informations supplémentaires. Ici, on va cliquer sur la méthode get transaction et pour le message, on va aussi faire un select sur le prix et sur le statut comme ceci. Maintenant, on va retourner dans notre composant chatbox à cet endroit-là et on peut voir qu'on n'avait même pas créé un composant de message. On va le faire tout de suite. Ici, on va copier ce code-là et nous allons créer un composant qu'on va appeler message item qui va donc renvoyer le contenu de notre message.

147:59 Et pour ce faire, nous allons récupérer ce type-là. On va copier ça et nous allons le mettre au singulier comme ceci. Bien sûr, on peut voir qu'on utilisait aussi les informations de l'utilisateur connecté. Donc, on va également recopier le hook use user comme ceci. Maintenant, on va pouvoir utiliser ce composant pour boucler dessus en passant comme argument notre message et on peut supprimer le reste du code et fermer ce composant comme ceci.

148:25 C'est quand même beaucoup plus lisible de cette manière. Et maintenant, on va pouvoir rajouter un peu de logique à l'intérieur de ce composant. Ici, on a donc un tableau qui contient le contenu et la date. Et ce qu'on aurait envie de faire, c'est de rajouter des actions ici. Donc potentiellement, un bouton de type Submit qui aurait comme variation green outline et qui dirait accepter et un autre bouton qui serait donc en rouge cette fois, Red outline et qui dirait refuser comme ceci.

148:52 Et bien sûr, on changerait de size et on mettrait le size s m pour que ça soit un peu plus petit. Maintenant, ce qu'on va faire avec ces deux boutons, c'est qu'on va les centrer à droite de notre application comme ceci. Là, on peut voir qu'on a un petit problème au niveau du design dans ces cas-là. Sauf que nous, on aura les boutons qui apparaissent seulement au cas où il y ait une offre et au cas où nous sommes capables de l'accepter. Donc ça ne va pas trop poser problème.

149:17 Ici, on va donc rajouter la condition. Si nous sommes le propriétaire de la transaction, dans ce cas-là, on va voir cette offre. Sinon, on ne va pas l'avoir comme ceci. Maintenant, on peut voir que les messages qu'on a envoyés ne contiennent plus cette offre. Sauf que ce n'est pas tout.

149:31 Bien sûr, on ne peut accepter une offre seulement si on nous a fait une offre. Donc pour ce message-là, on ne peut pas l'accepter non plus. Donc on va rajouter un morceau de logique et message point status est égal à transaction message status, donc le énum qu'on va importer et si le statut est égal à peine une offre, alors dans ce cas-là, on va afficher ces boutons. Bien sûr, si l'offre a été refusée ou acceptée, on ne va plus non plus afficher ces boutons-là. Donc maintenant, ce qu'on va faire, c'est qu'on va utiliser deux pour accepter et refuser la transaction.

150:04 Le premier, on va appeler accepte offert-fetcher et le deuxième, on va l'appeler decline offert-fetcher et on va faire exactement ce qu'on a fait tout à l'heure parce que ces deux actions vont s'effectuer dans le même fichier. Ici, si on retourne dans notre action, on peut voir qu'on avait déjà créé ce schéma-là avec une action ici et un prix et un contenu. Sauf que là, nous allons recréer un schéma qu'on va appeler par exemple déclin of her skima qui sera un objet avec seulement une action qui sera de type decline. Et donc, on va la rajouter à notre zod point union. Et nous allons faire la même chose pour le accepte offert skima comme ceci, ça sera un schéma dole avec une seule propriété qui va s'appeler accepte comme ceci.

150:50 Maintenant, on va la rajouter à cet endroit-là et ici dans notre case, on peut voir qu'on peut retrouver les deux cas accepte et decline. On va donc tout de suite les rajouter comme ceci et nous allons maintenant devoir ajouter un morceau de logique pour ces deux possibilités. Et ça, c'est vraiment la puissance de remix, de conforme et de zod. C'est-à-dire que dans un même fichier, sur une même route API, là, on est en train d'exécuter quatre mutations différentes. Soit elle envoie un message, soit l'envoi d'une offre, soit accepte, soit décline.

151:23 Et c'est vraiment très puissant. Ici, pour ces composants-là, on n'a pas besoin d'utiliser le hook use form vu qu'on utilise seulement les actions. On peut donc tout simplement rajouter notre formulaire avec un accepte offert fetcher en forme comme ceci et juste préciser méthode post et c'est tout ce qui nous reste à faire. Ici pour notre bouton, on doit maintenant rajouter un de la valeur action et une valeur de type accepte comme ceci. Et maintenant, on va faire exactement la même chose pour le bouton du dessous.

151:53 On va utiliser le point forme avec la méthode post et un et une value. Cette fois, la value prendra comme valeur comme ceci. Maintenant, on va rajouter quand même un petit flex. Et maintenant, ce qu'on va faire, c'est qu'on va ajouter des consoles log ici dans le accepte et dans le déclin et nous allons faire un console log de sunmission point value pour voir si ça fonctionne bien. Donc, on va ouvrir notre terminal ici et nous allons cliquer sur accepte et sur refuser.

152:25 Et là, on peut voir qu'on a nos deux actions qui sont bien console loguées avec notre return json pour réinitialiser le formulaire au cas où il y a un formulaire. Ça marche très bien. Donc maintenant, on va implémenter la logique de ces deux dernières actions. Mais pour l'implémenter, je viens d'oublier un petit détail, on a quand même besoin de savoir quel message doit être refusé ici parce qu'on peut voir qu'il y a deux offres qui ont été faites. Donc on va quand même rajouter un champ qu'on va appeler message ID et ça sera donc un string avec un champ requis qu'on va appeler vous devez fournir le message ID.

152:59 Mais ce message d'erreur n'est pas censé apparaître aux utilisateurs. Donc ici, on va retourner dans nos deux formulaires et on va rajouter un input de type idem et on va tout simplement intégrer ce formulaire sans utiliser conforme avec un name message ID et en value le message point ID comme ceci. On va rajouter ce champ-là dans le deuxième formulaire comme ceci et maintenant on va réessayer, on va accepter l'offre, on va refuser l'offre et là on retrouve bien le message ID qui nous est au final nécessaire pour pouvoir changer le statut de ce message-là. Donc maintenant que c'est fait, on va retourner dans le fichier transactions point serveur et nous allons créer deux méthodes supplémentaires. Ici, on va donc dupliquer la méthode sandMessage et cette fois, nous allons l'appeler accepte transaction offer.

153:45 Ça va donc prendre un transaction ID et ça va prendre un message ID sera sous forme de string et ça ne va pas prendre un content. Et maintenant, on va donc faire un update du message et on va dire que le status est donc message status point accepted sur le message ID. Sauf qu'on va rajouter une sécurité dans le ware ici. Dans le ware, on va donc récupérer l'ID du message, mais on va aussi récupérer le transaction ID pour être sûr que ce message fasse bien partie de notre transaction ici. Et on va récupérer donc transaction point ID comme ceci.

154:20 Et maintenant, on va donc dupliquer cette méthode, qu'on va donc appeler decline transaction offer et ça va être exactement le même code, sauf qu'on va changer la valeur de l'inhum ici en rejectd offer comme ceci. Et maintenant, on peut retourner dans notre composant tout en haut et importer ces deux méthodes. Donc lorsqu'on accepte la transaction, on va exécuter la méthode accepte transaction offer comme ceci, avec le contexte, le message ID, le user ID et le transaction ID comme ceci. Et pour le reçu, ça va être exactement la même chose, mais on va renommer la fonction decline transaction offert comme ceci et on va faire un petit essai. Donc pour la première offre, on va la refuser comme ceci et là on peut voir qu'il n'y a plus les boutons qui apparaissent.

155:06 Par contre, j'ai l'impression que ça a changé l'ordre des messages. Donc pour y remédier, on va retourner tout en haut ici et on va cliquer sur la méthode get transaction et sur les messages ici, on va faire un order sur le created at en descendant ou plutôt en ascendant pour avoir les premiers messages en premier ici, parce que c'est cette offre a été refusée. Maintenant, on va retourner dans notre composant ici et ça commence à être pas très lisible. Donc ce qu'on va faire dans notre message item ici, c'est qu'on va copier cette logique et on va en créer un composant à l'intérieur de notre composant qu'on va appeler user action. Et ça ne va rien prendre comme argument, mais nous allons rajouter une première condition.

155:48 Ici, on peut voir qu'on avait donc, là on va return un fragment et on peut voir qu'on avait cette action-là. Donc, si on est propriétaire de l'offre et que le statut est égal à pending, alors nous affichons ta. On va reformater ce code. Déjà, si le message en user ID est différent du user ID, alors on va return lol. Dans ce cas-là, on est plus obligé de rajouter cette première logique.

156:11 Maintenant, on va mettre que if, le statut est égal à pending offert, alors si le statut est pending, alors dans ce cas-là, on va re-terner ce dip-là comme ceci, donc on fait notre re-terme du composant qu'on connaît déjà. Par contre, si le statut est différent, alors dans ce cas-là, nous allons tout simplement afficher l'état du statut. Donc offre et on va faire une condition. Si c'est égal à accepter l'offre, on va mettre accepter. Sinon, si c'est égal à transaction message status point rejectd, alors on va mettre refuser et sinon on va mettre inconnu.

156:45 Non reconnu plutôt. Et maintenant, on peut supprimer ce morceau de code et nous allons utiliser notre composant user action au lieu de nos deux fetcher ici. Et on va le rajouter à cet endroit-là. On va supprimer ça et on va rajouter notre user action. Et là, on peut voir qu'on a donc nos deux statuts qui sont affichées.

157:02 Salut, ça va, offre non reconnue. Sauf que là, on a offre non reconnue pour tous ces statuts, ce qui est né évidemment par ce qu'on voulait parce que là, le message user ID est égal au user ID, donc c'est tout de suite passé. Donc ici, on doit rajouter une deuxième condition. La deuxième condition, c'est if message point price n'existe pas, alors on va également return nul. Et là, ça va régler tous nos problèmes parce qu'on retrouve seulement les messages qui ont donc été acceptés ou refusés.

157:32 Ici, c'est vrai que ce code, il n'est pas trop visible. On peut le remplacer par des ifs. Si l'offre est acceptée, on renvoie donc un spam offre acceptée. On peut même rajouter un petit peu le style text green text x s comme ceci. Si le statut, c'est Rejectd offert, alors dans ce cas-là, on va mettre du rouge offre refusée.

157:51 Et sinon, on n'est normalement pas censé se retrouver dans cette condition. On va mettre statut inconnu et on va bien sûr rajouter un petit peu le style et on va mettre du rouge très vif. Maintenant, ici, on retrouve bien le statut de la première offre. Peut voir que l'offre a été refusée en un coup d'oeil. Et si on clique maintenant sur accepter, ici, on peut voir que le statut est passé en offre acceptée.

158:13 On va juste changer un peu les couleurs comme ceci. Et maintenant, on peut continuer d'envoyer des messages bonjour, comment vas-tu Cet écran m'a l'air plutôt complet. Alors bien sûr, on pourrait rajouter beaucoup de logique. Par exemple, on pourrait rajouter l'édition des messages pour corriger notre faute. On pourrait afficher conditionnellement ce composant dans le cas où une offre est déjà été faite ou une offre est déjà acceptée et on pourrait, on pourrait également potentiellement modifier une offre qu'on a faite à l'utilisateur.

158:42 Mais pour le moment, on va rester très simple et on va aller maintenant à l'édition du profil de l'utilisateur. Effectivement, on peut voir là que j'ai cliqué sur l'onglet profile et il n'a pas encore été créé. Et ce qui serait cool, c'est de pouvoir créer des avatars pour de vrai comme celui-ci ici. Donc, on va créer cette page maintenant. On va donc dans nos fichiers ici, dans frontend root underscore public et nous allons rajouter un fichier qu'on va appeler profil point PFX.

159:12 Et déjà, ce qu'on va faire, c'est qu'on va exporter une méthode l'odeur comme ceci pour effacer cette erreur. Et maintenant, nous allons nous inspirer de la page login ici et nous allons exporter ce composant-là comme ceci sur la page profil. On va donc importer les formulaires, les méthodes getform props, les field, les méthodes get input props et le bouton. Et bien sûr, on va également importer l'action du formulaire. En fait, on va tout importer comme sur le formulaire bien sûr, et on va changer toutes les méthodes.

159:43 Mais ça va rester la même logique. Sauf qu'on va utiliser un schéma un peu différent comme chez Amazon, on l'avait défini ici, on va donc pouvoir potentiellement modifier l'e-mail comme ceci, mais on va aussi pouvoir modifier le first name comme ceci par exemple. On va donc changer le schéma. On va l'appeler Edit profile skima comme ceci et on va commenter le super refine pour le moment. Déjà, on a envie de s'assurer que tout le composant fonctionne.

160:10 On va utiliser conforme, on va réimporter toutes ces données et on va utiliser le schéma Edit profile comme ceci et on va en h1 mettre au fil comme ceci. Ça m'a l'air de ressembler à quelque chose. Maintenant, dans notre méthode Loader, ce qu'on va faire déjà, c'est que c'est une route protégée. Donc, on va appeler la méthode await require user comme ceci. Il va donc prendre comme argument un contexte.

160:33 Mais on peut voir que cette méthode nous renvoie un email et un name. Donc, pour le moment, on n'a pas besoin de plus à afficher pour ce profil utilisateur. Donc, on va récupérer les informations de notre utilisateur dans une variable user et on va ensuite leur envoyer sous forme de JSON comme ceci côté client. Maintenant, ici, on voit bien que le champ s'appelle donc name. Donc, on peut renommer notre schéma ZON.

160:55 Et ici, tout en bas côté client, on va rajouter un champ. Ça sera donc le name, ça sera le type texte, prénom. Et maintenant, on peut rajouter des defold value à notre formulaire pour chacune des valeurs. Donc sur l'e-mail, ça sera le user point email et sur le name, ça sera le user point name et on peut récupérer ces informations avec un use loader data ou alors avec notre hook use user. Mais pour le moment, on va récupérer les informations fraîches qui viennent du serveur comme ceci.

161:27 Et là, on peut voir que ça a bien prérempli nos champs. Alors c'est vrai que changer l'email, ça peut être compliqué parce que ça va du coup changer la manière dont l'utilisateur se connecte. Alors qu'est-ce qu'on fait s'il change son email Est-ce qu'on le déconnecte Est-ce qu'on le fait cliquer sur un lien pour qu'il puisse changer son email Ou est-ce qu'on active le champ de manière libre comme celui-ci C'est votre décision les amis. En tout cas, on va aussi renommer notre page, on va l'appeler user profile et maintenant on va retourner ici côté serveur dans notre action et nous pouvons décommenter ce super et nous allons rajouter cette même logique. On va vérifier que l'utilisateur existe, mais sans le mot de passe.

162:05 Donc on ne va pas tout mettre de mot de passe et au cas où l'utilisateur existe avec cet email et que l'email est différent de l'email actif de l'utilisateur, dans ce cas-là, bien sûr, on va afficher un message d'erreur. Donc déjà, comme c'est une action, on va rajouter un require user comme ceci avec notre contexte et on va récupérer les informations de l'utilisateur identifié et nous allons faire un if email est différent de user email, ça veut dire que l'utilisateur a cherché à modifier son email dans le champ. Donc s'il est différent, on va rajouter ce morceau de logique dans notre vérification. Ensuite ici, on peut supprimer tout le reste de ce code et à la place on va renvoyer pour le moment un json en submission replay et on va aller maintenant dans le fichier profile point serveur et nous allons ajouter une nouvelle méthode qui va s'appeler edit profile comme ceci donc ça va prendre un contexte et bien sûr le schéma zot que nous avons créé Si le schéma s'appelle Elite profile SKEMA, donc on va l'exporter et on va maintenant l'importer et on va le nommer profile data et nous allons modifier les informations de l'utilisateur sur le Uther ID comme ceci et nous allons modifier son email et son name.

163:19 Et cette fois, on peut être sûr que l'email, il peut être de manière safe modifié parce qu'on a fait un check tout à l'heure pour vérifier que cet email existe ou non. Par contre, on n'a pas besoin de faire un check sur le nom. Donc, à cet endroit, pendant de return notre formulaire, on peut appeler notre méthode edit profile comme ceci. Il prend donc un contexte, le profile data est donc égal à la sommation point vview et notre user eddy et donc l'ID de l'utilisateur connecté. Et maintenant, on va donc faire un essai.

163:48 Là, je vais me renommer et je vais m'appeler Bertrand, on va modifier et là, on peut voir que ça a même changé instantanément dans la nappe barre mon nom et on peut voir que ça ne m'a pas mis l'erreur. Cet email existe déjà. Par contre, si j'avais mis Virgile trois arobase algo max, ici, là, on peut voir que j'ai une erreur parce qu'un email existe déjà pour Virgile trois algo max sur le champ email. Et on peut voir que ce schéma Zod ne m'a pas du tout protégé en fait, parce que ça n'a pas envoyé d'erreur. Donc, on a un petit bug et on va devoir le corriger.

164:19 Ici, on va ouvrir notre terminal et nous allons console loguer l'email du formulaire et le user email qui est égal à user en email. Et on va également console loguer existine user. Maintenant, on va refaire une tentative de modification d'email tout en ayant notre terminal d'ouvert. On va cliquer sur modifier et là, on peut voir des informations intéressantes. L'email est donc virgile trois ragomax point f r, qui est donc différent de l'email de l'utilisateur connecté et on a même le de l'existim user.

164:48 Avec le champ is existe une user pour erreur, s'il y a une erreur, alors on affiche, l'utilisateur existe. Sauf que ce n'est ce qu'on veut de nous, parce que là, on peut voir que l'erreur est à false. En fait, c'est nous qui avions fait cette logique un peu plus tôt. Si on regarde notre méthode check if user existe, on va donc cliquer sur hosts point service ici, et on peut regarder que ça renvoie false si l'utilisateur existe. Donc justement, on aurait dû nous y attendre et pas copier coller bêtement ce morceau de code.

165:16 En fait, c'est si l'erreur est à false, on va ajouter une issue et qu'on va return false comme ceci pour donc protéger et empêcher l'édition de notre email. Maintenant, on va donc pouvoir enregistrer les informations et si on met bien virgile trois at algo max, on a donc ce message d'erreur, l'utilisateur existe. Et pour ajouter un peu plus de contexte, on devrait plutôt mettre le message cet email est déjà utilisé comme ceci. Maintenant, si on refait une édition sur Virgile trois, on peut voir l'erreur cet email est déjà utilisé. Voilà donc, on peut maintenant effacer ce console log et si je change mon email et que je l'appelle par exemple contact at algomax point f r, là on peut voir que ça a bien changé notre email.

165:59 Je peux maintenant me déconnecter. Si je fais une connexion sur contact at algomax point f r et ABC un e trois, je me reconnecte bien au compte de Bertrand ici. Je vais donc me renommer en Virgile et reprendre cet email là à nouveau comme ceci et tout revient à la normale.

Vidéos similaires

Rejoins la

newsletter