Bruno Predot

Création du composant MovieCommentForm, début de mise en place de la lofgique, a…

…vec rules de validation.
  1 +<script lang="ts" setup>
  2 +import { useVuelidate } from "@vuelidate/core";
  3 +import { helpers, maxLength, minLength, required, minValue, maxValue } from "@vuelidate/validators";
  4 +
  5 +// type FormData = {
  6 +// username: string
  7 +// message: string
  8 +// rating: number
  9 +// };
  10 +
  11 +type FormData = { [key: string]: string|number };
  12 +
  13 +//#region --Data/ref--.
  14 +const initialState = {
  15 + username: "",
  16 + message: "",
  17 + rating: 5,
  18 +};
  19 +
  20 +// Validation rules
  21 +const rules = {
  22 + username: {
  23 + required: helpers.withMessage("Le nom d'utilisateur est requis", required),
  24 + minLength: helpers.withMessage("Le nom d'utilisateur doit contenir au moins 3 caractères", minLength(3)),
  25 + maxLength: helpers.withMessage("Le nom d'utilisateur ne peut pas dépasser 50 caractères", maxLength(50)),
  26 + alpha: helpers.withMessage(
  27 + "Le nom d'utilisateur ne peut contenir que des lettres",
  28 + helpers.regex(/^[a-zA-ZÀ-ÿ\s]+$/),
  29 + ),
  30 + },
  31 + message: {
  32 + required: helpers.withMessage("Le message est requis", required),
  33 + minLength: helpers.withMessage("Le message doit contenir au moins 3 caractères", minLength(3)),
  34 + maxLength: helpers.withMessage("Le message ne peut pas dépasser 500 caractères", maxLength(500)),
  35 + },
  36 + rating:{
  37 + required: helpers.withMessage("La notation est requise", required),
  38 + minValue: helpers.withMessage("Le message ne être inférieure à 0", minValue(0)),
  39 + maxValue: helpers.withMessage("Le message ne être suppérieur à 10", maxValue(10)),
  40 + }
  41 +};
  42 +
  43 +const formData = reactive({
  44 + ...initialState,
  45 +});
  46 +const v$ = useVuelidate(rules, formData);
  47 +//#endregion
  48 +
  49 +function submitComment() {
  50 + console.log("Submit !")
  51 +}
  52 +
  53 +function clear () {
  54 + v$.value.$reset();
  55 + formData.username = initialState.username;
  56 + formData.message = initialState.message;
  57 + formData.rating = initialState.rating;
  58 +}
  59 +
  60 +// :error-messages="getErrors('username', $v.username)" :counter="10" label="username"
  61 +// :error-messages="getErrors('message', $v.message)"
  62 +</script>
  63 +
  64 +<template>
  65 + <section>
  66 + movie comment form
  67 + <VForm @submit.prevent="submitComment">
  68 + <v-text-field
  69 + v-model="formData.username"
  70 + :error-messages="v$.username.$errors.map(e => e.$message)"
  71 + label="nom d'utilisateur"
  72 + placeholder="nom d'utilisateur"
  73 + required
  74 + @blur="v$.username.$touch()"
  75 + @input="v$.username.$touch()"
  76 + />
  77 + <v-textarea
  78 + v-model="formData.message"
  79 + :error-messages="v$.message.$errors.map(e => e.$message)"
  80 + label="message"
  81 + placeholder="Saisissez votre commentaire"
  82 + required
  83 + @blur="v$.message.$touch"
  84 + @input="v$.message.$touch"
  85 + />
  86 + <v-text-field
  87 + v-model="formData.rating"
  88 + :error-messages="v$.rating.$errors.map(e => e.$message)"
  89 + label="Note (1-10)"
  90 + placeholder=""
  91 + required
  92 + type="number"
  93 + @blur="v$.rating.$touch"
  94 + @input="v$.rating.$touch"
  95 + />
  96 + <v-btn class="mr-4">submit</v-btn>
  97 + </VForm>
  98 + </section>
  99 +</template>
  100 +
  101 +<style scoped></style>
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 "@nuxt/ui": "^2.22.0", 17 "@nuxt/ui": "^2.22.0",
18 "@pinia-orm/nuxt": "^1.10.2", 18 "@pinia-orm/nuxt": "^1.10.2",
19 "@pinia/nuxt": "^0.9.0", 19 "@pinia/nuxt": "^0.9.0",
  20 + "@types/vuelidate": "^0.7.22",
20 "@unhead/vue": "^2.0.8", 21 "@unhead/vue": "^2.0.8",
21 "@vuelidate/core": "^2.0.3", 22 "@vuelidate/core": "^2.0.3",
22 "@vuelidate/validators": "^2.0.4", 23 "@vuelidate/validators": "^2.0.4",
@@ -4636,6 +4637,64 @@ @@ -4636,6 +4637,64 @@
4636 "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", 4637 "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
4637 "license": "MIT" 4638 "license": "MIT"
4638 }, 4639 },
  4640 + "node_modules/@types/vuelidate": {
  4641 + "version": "0.7.22",
  4642 + "resolved": "https://registry.npmjs.org/@types/vuelidate/-/vuelidate-0.7.22.tgz",
  4643 + "integrity": "sha512-bD3pP9FgL3pxMVQ9NJ3d8BbV8Ij6xsrDKdCO4l1Wq/AksXxRRmQ9lmYjRJwn/hLMcgWO/k0QdULfZWpRz13adw==",
  4644 + "license": "MIT",
  4645 + "dependencies": {
  4646 + "vue": "^2.7.15"
  4647 + }
  4648 + },
  4649 + "node_modules/@types/vuelidate/node_modules/@vue/compiler-sfc": {
  4650 + "version": "2.7.16",
  4651 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
  4652 + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
  4653 + "dependencies": {
  4654 + "@babel/parser": "^7.23.5",
  4655 + "postcss": "^8.4.14",
  4656 + "source-map": "^0.6.1"
  4657 + },
  4658 + "optionalDependencies": {
  4659 + "prettier": "^1.18.2 || ^2.0.0"
  4660 + }
  4661 + },
  4662 + "node_modules/@types/vuelidate/node_modules/prettier": {
  4663 + "version": "2.8.8",
  4664 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
  4665 + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
  4666 + "license": "MIT",
  4667 + "optional": true,
  4668 + "bin": {
  4669 + "prettier": "bin-prettier.js"
  4670 + },
  4671 + "engines": {
  4672 + "node": ">=10.13.0"
  4673 + },
  4674 + "funding": {
  4675 + "url": "https://github.com/prettier/prettier?sponsor=1"
  4676 + }
  4677 + },
  4678 + "node_modules/@types/vuelidate/node_modules/source-map": {
  4679 + "version": "0.6.1",
  4680 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
  4681 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
  4682 + "license": "BSD-3-Clause",
  4683 + "engines": {
  4684 + "node": ">=0.10.0"
  4685 + }
  4686 + },
  4687 + "node_modules/@types/vuelidate/node_modules/vue": {
  4688 + "version": "2.7.16",
  4689 + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
  4690 + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
  4691 + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
  4692 + "license": "MIT",
  4693 + "dependencies": {
  4694 + "@vue/compiler-sfc": "2.7.16",
  4695 + "csstype": "^3.1.0"
  4696 + }
  4697 + },
4639 "node_modules/@types/web-bluetooth": { 4698 "node_modules/@types/web-bluetooth": {
4640 "version": "0.0.21", 4699 "version": "0.0.21",
4641 "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", 4700 "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 "@nuxt/ui": "^2.22.0", 23 "@nuxt/ui": "^2.22.0",
24 "@pinia-orm/nuxt": "^1.10.2", 24 "@pinia-orm/nuxt": "^1.10.2",
25 "@pinia/nuxt": "^0.9.0", 25 "@pinia/nuxt": "^0.9.0",
  26 + "@types/vuelidate": "^0.7.22",
26 "@unhead/vue": "^2.0.8", 27 "@unhead/vue": "^2.0.8",
27 "@vuelidate/core": "^2.0.3", 28 "@vuelidate/core": "^2.0.3",
28 "@vuelidate/validators": "^2.0.4", 29 "@vuelidate/validators": "^2.0.4",
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 //#region --import--. 2 //#region --import--.
3 -import { ArrowLeftIcon, FilmIcon } from "lucide-vue-next"; 3 +import { ArrowLeftIcon } from "lucide-vue-next";
4 import { useTMDB } from "~/composables/tMDB"; 4 import { useTMDB } from "~/composables/tMDB";
5 import { computed, onMounted, ref } from "vue"; 5 import { computed, onMounted, ref } from "vue";
6 import { Movie } from "~/models/movie"; 6 import { Movie } from "~/models/movie";
@@ -125,7 +125,7 @@ onMounted(() => { @@ -125,7 +125,7 @@ onMounted(() => {
125 <!-- Contenu du film --> 125 <!-- Contenu du film -->
126 <div v-else-if="movie" class="relative"> 126 <div v-else-if="movie" class="relative">
127 <!-- Backdrop image --> 127 <!-- Backdrop image -->
128 - <ui-components-backdrop-image v-if="movie.backdrop_path" :src="movie.poster_path" :title="movie.title" /> 128 + <ui-components-backdrop-image v-if="movie.backdrop_path" :src="movie.backdrop_path" :title="movie.title" />
129 129
130 <!-- Contenu principal --> 130 <!-- Contenu principal -->
131 <div class="container mx-auto px-4 py-8 relative z-10 pt-20"> 131 <div class="container mx-auto px-4 py-8 relative z-10 pt-20">
@@ -170,12 +170,14 @@ onMounted(() => { @@ -170,12 +170,14 @@ onMounted(() => {
170 <span class="font-semibold">Têtes d'affiche:</span> 170 <span class="font-semibold">Têtes d'affiche:</span>
171 {{ 171 {{
172 movie.credit.cast 172 movie.credit.cast
173 - .slice(0, 5) 173 + .slice(0, 10)
174 .map((person) => person.name) 174 .map((person) => person.name)
175 .join(", ") 175 .join(", ")
176 }} 176 }}
177 </div> 177 </div>
178 </div> 178 </div>
  179 + <!-- Comments form. -->
  180 + <form-movie-comment-form />
179 </section> 181 </section>
180 </div> 182 </div>
181 </div> 183 </div>