Bruno Predot

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

…vec rules de validation.
<script lang="ts" setup>
import { useVuelidate } from "@vuelidate/core";
import { helpers, maxLength, minLength, required, minValue, maxValue } from "@vuelidate/validators";
// type FormData = {
// username: string
// message: string
// rating: number
// };
type FormData = { [key: string]: string|number };
//#region --Data/ref--.
const initialState = {
username: "",
message: "",
rating: 5,
};
// Validation rules
const rules = {
username: {
required: helpers.withMessage("Le nom d'utilisateur est requis", required),
minLength: helpers.withMessage("Le nom d'utilisateur doit contenir au moins 3 caractères", minLength(3)),
maxLength: helpers.withMessage("Le nom d'utilisateur ne peut pas dépasser 50 caractères", maxLength(50)),
alpha: helpers.withMessage(
"Le nom d'utilisateur ne peut contenir que des lettres",
helpers.regex(/^[a-zA-ZÀ-ÿ\s]+$/),
),
},
message: {
required: helpers.withMessage("Le message est requis", required),
minLength: helpers.withMessage("Le message doit contenir au moins 3 caractères", minLength(3)),
maxLength: helpers.withMessage("Le message ne peut pas dépasser 500 caractères", maxLength(500)),
},
rating:{
required: helpers.withMessage("La notation est requise", required),
minValue: helpers.withMessage("Le message ne être inférieure à 0", minValue(0)),
maxValue: helpers.withMessage("Le message ne être suppérieur à 10", maxValue(10)),
}
};
const formData = reactive({
...initialState,
});
const v$ = useVuelidate(rules, formData);
//#endregion
function submitComment() {
console.log("Submit !")
}
function clear () {
v$.value.$reset();
formData.username = initialState.username;
formData.message = initialState.message;
formData.rating = initialState.rating;
}
// :error-messages="getErrors('username', $v.username)" :counter="10" label="username"
// :error-messages="getErrors('message', $v.message)"
</script>
<template>
<section>
movie comment form
<VForm @submit.prevent="submitComment">
<v-text-field
v-model="formData.username"
:error-messages="v$.username.$errors.map(e => e.$message)"
label="nom d'utilisateur"
placeholder="nom d'utilisateur"
required
@blur="v$.username.$touch()"
@input="v$.username.$touch()"
/>
<v-textarea
v-model="formData.message"
:error-messages="v$.message.$errors.map(e => e.$message)"
label="message"
placeholder="Saisissez votre commentaire"
required
@blur="v$.message.$touch"
@input="v$.message.$touch"
/>
<v-text-field
v-model="formData.rating"
:error-messages="v$.rating.$errors.map(e => e.$message)"
label="Note (1-10)"
placeholder=""
required
type="number"
@blur="v$.rating.$touch"
@input="v$.rating.$touch"
/>
<v-btn class="mr-4">submit</v-btn>
</VForm>
</section>
</template>
<style scoped></style>
\ No newline at end of file
... ...
... ... @@ -17,6 +17,7 @@
"@nuxt/ui": "^2.22.0",
"@pinia-orm/nuxt": "^1.10.2",
"@pinia/nuxt": "^0.9.0",
"@types/vuelidate": "^0.7.22",
"@unhead/vue": "^2.0.8",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
... ... @@ -4636,6 +4637,64 @@
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
"license": "MIT"
},
"node_modules/@types/vuelidate": {
"version": "0.7.22",
"resolved": "https://registry.npmjs.org/@types/vuelidate/-/vuelidate-0.7.22.tgz",
"integrity": "sha512-bD3pP9FgL3pxMVQ9NJ3d8BbV8Ij6xsrDKdCO4l1Wq/AksXxRRmQ9lmYjRJwn/hLMcgWO/k0QdULfZWpRz13adw==",
"license": "MIT",
"dependencies": {
"vue": "^2.7.15"
}
},
"node_modules/@types/vuelidate/node_modules/@vue/compiler-sfc": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
"integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
"dependencies": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
},
"optionalDependencies": {
"prettier": "^1.18.2 || ^2.0.0"
}
},
"node_modules/@types/vuelidate/node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"license": "MIT",
"optional": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/@types/vuelidate/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@types/vuelidate/node_modules/vue": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"license": "MIT",
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
... ...
... ... @@ -23,6 +23,7 @@
"@nuxt/ui": "^2.22.0",
"@pinia-orm/nuxt": "^1.10.2",
"@pinia/nuxt": "^0.9.0",
"@types/vuelidate": "^0.7.22",
"@unhead/vue": "^2.0.8",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
... ...
<script lang="ts" setup>
//#region --import--.
import { ArrowLeftIcon, FilmIcon } from "lucide-vue-next";
import { ArrowLeftIcon } from "lucide-vue-next";
import { useTMDB } from "~/composables/tMDB";
import { computed, onMounted, ref } from "vue";
import { Movie } from "~/models/movie";
... ... @@ -125,7 +125,7 @@ onMounted(() => {
<!-- Contenu du film -->
<div v-else-if="movie" class="relative">
<!-- Backdrop image -->
<ui-components-backdrop-image v-if="movie.backdrop_path" :src="movie.poster_path" :title="movie.title" />
<ui-components-backdrop-image v-if="movie.backdrop_path" :src="movie.backdrop_path" :title="movie.title" />
<!-- Contenu principal -->
<div class="container mx-auto px-4 py-8 relative z-10 pt-20">
... ... @@ -170,12 +170,14 @@ onMounted(() => {
<span class="font-semibold">Têtes d'affiche:</span>
{{
movie.credit.cast
.slice(0, 5)
.slice(0, 10)
.map((person) => person.name)
.join(", ")
}}
</div>
</div>
<!-- Comments form. -->
<form-movie-comment-form />
</section>
</div>
</div>
... ...