Add localization support for various components, including dialogs and views, in English, Spanish, and French. Implemented translations for user profile management, payment processes, and creator functionalities. Updated existing components to utilize the new translation system.

This commit is contained in:
2025-04-18 04:21:52 -04:00
parent a559611e04
commit d6c3bd7fa4
137 changed files with 1230 additions and 614 deletions

View File

@@ -0,0 +1,4 @@
{
"title": "Login",
"alt": "hutopy login"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Iniciar sesión",
"alt": "iniciar sesión hutopy"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Connexion",
"alt": "connexion hutopy"
}

View File

@@ -2,11 +2,9 @@
import {ref} from 'vue';
import {GoogleLogin} from "vue3-google-login";
import {useAuthStore} from '@/stores/authStore.js';
import {useTranslations} from '@/translations/translations';
import {useFacebookLogin} from "@/composables/useFacebookLogin";
import Facebook from "@/views/svg/Facebook.vue";
const {loginWithFacebook} = useFacebookLogin();
const t = useTranslations();
const authStore = useAuthStore();
@@ -22,21 +20,14 @@ async function googleCallback(token) {
</script>
<template>
<div class="flex min-h-full justify-center items-center p-20 w-full">
<div class="card justify-items-center">
<img alt="hutopy login"
<img :alt="t('alt')"
src="/images/hutopymedia/loginpage/hutopylogin.svg"/>
<div class="flex flex-col gap-10">
<h1 class="text-2xl font-bold login-text text-center ">
Connexion
{{ t('title') }}
</h1>
<div class="flex flex-col gap-4">
<google-login :callback="googleCallback"
popup-type="TOKEN">
@@ -45,24 +36,11 @@ async function googleCallback(token) {
Google
</button>
</google-login>
<!-- <button class="secondary"-->
<!-- @click="loginWithFacebook">-->
<!-- <facebook class="social-icon"></facebook>-->
<!-- Facebook-->
<!-- </button>-->
</div>
</div>
</div>
</div>
</template>
<style scoped>
.social-icon {
@apply w-5 h-5;
@apply text-base;
}
</style>

View File

@@ -0,0 +1,7 @@
{
"title": "Payment Successful!",
"message": "Your payment has been processed successfully.",
"usernameDefault": "the creator",
"receipt": "A receipt has been sent to your email.",
"continue": "Continue to"
}

View File

@@ -0,0 +1,7 @@
{
"title": "¡Pago exitoso!",
"message": "Su pago ha sido procesado con éxito.",
"usernameDefault": "el creador",
"receipt": "Se ha enviado un recibo a su correo electrónico.",
"continue": "Continuar a"
}

View File

@@ -0,0 +1,7 @@
{
"title": "Paiement réussi !",
"message": "Votre paiement a été traité avec succès.",
"usernameDefault": "le créateur",
"receipt": "Un reçu a été envoyé à votre adresse e-mail.",
"continue": "Continuer vers"
}

View File

@@ -3,7 +3,7 @@
<div class="card">
<h1>
{{ $t('paymentConfirmation.success.title') }}
{{ t('title') }}
</h1>
<p>
@@ -13,28 +13,28 @@
</p>
<p>
{{ $t('paymentConfirmation.success.message') }}
{{ t('message') }}
<span v-if="creatorUserName">
{{ creatorUserName }}
</span>
<span v-else>
{{ $t('paymentConfirmation.success.usernameDefault') }}
{{ t('usernameDefault') }}
</span>
</p>
<p>
{{ $t('paymentConfirmation.success.receipt') }}
{{ t('receipt') }}
</p>
<!-- Continue Button -->
<div class="card-actions">
<button
class="action-button"
@click="router.push({ path: `/@${creatorUserName}` })">
{{ $t('paymentConfirmation.success.continue') }}
@click="goBack()">
{{ t('continue') }}
</button>
</div>
@@ -44,25 +44,21 @@
</template>
<script setup>
import {useClient} from '@/plugins/api.js';
import {onBeforeMount, ref} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {useRouter, useRoute} from 'vue-router';
import {useTranslations} from '@/translations/translations';
const router = useRouter();
const route = useRoute();
const client = useClient();
const t = useTranslations();
const creatorId = route.params.creatorId;
const creatorUserName = ref('');
onBeforeMount(async () => {
try {
const response = await client.get(`/api/creators/${creatorId}`);
creatorUserName.value = response.data.name;
} catch (error) {
console.error('Failed to fetch creator data:', error);
function goBack() {
const returnUrl = route.query.returnUrl;
if (returnUrl) {
router.push(returnUrl);
} else {
router.back();
}
});
}
</script>

View File

@@ -0,0 +1,5 @@
{
"title": "Payment Failed",
"message": "We couldn't process your payment. Please try again or contact support if the problem persists.",
"continue": "Return to"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Pago fallido",
"message": "No pudimos procesar su pago. Por favor, inténtelo de nuevo o contacte con soporte si el problema persiste.",
"continue": "Volver a"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Échec du paiement",
"message": "Nous n'avons pas pu traiter votre paiement. Veuillez réessayer ou contacter le support si le problème persiste.",
"continue": "Retourner à"
}

View File

@@ -1,29 +1,11 @@
<template>
<div class="container">
<div class="card">
<h1>
{{ $t('paymentConfirmation.failure.title') }}
</h1>
<p>
<v-icon size="120" color="error">
mdi-close-circle
</v-icon>
</p>
<p>
{{ $t('paymentConfirmation.failure.message') }}
</p>
<h1>{{ t('title') }}</h1>
<p>{{ t('message') }}</p>
<div class="card-actions">
<button
class="action-button"
@click="router.push({ path: `/@${creatorUserName}` })"
>
{{ $t('paymentConfirmation.success.continue') }}
{{ creatorUserName }}
<button class="action-button" @click="goBack()">
{{ t('continue') }}
</button>
</div>
</div>
@@ -31,25 +13,21 @@
</template>
<script setup>
import {useClient} from '@/plugins/api.js';
import {onBeforeMount, ref} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import { useRouter, useRoute } from 'vue-router';
import { useTranslations } from '@/translations/translations';
const router = useRouter();
const route = useRoute();
const client = useClient();
const t = useTranslations();
const creatorId = route.params.creatorId;
const creatorUserName = ref('');
onBeforeMount(async () => {
try {
const response = await client.get(`/api/creators/${creatorId}`);
creatorUserName.value = response.data.name;
} catch (error) {
console.error('Failed to fetch creator data:', error);
function goBack() {
const returnUrl = route.query.returnUrl;
if (returnUrl) {
router.push(returnUrl);
} else {
router.back();
}
});
}
</script>
<style scoped>

View File

@@ -0,0 +1,5 @@
{
"message": "Message (optional)",
"amount": "Amount ($)",
"send": "Send"
}

View File

@@ -0,0 +1,5 @@
{
"message": "Mensaje (opcional)",
"amount": "Cantidad ($)",
"send": "Enviar"
}

View File

@@ -0,0 +1,5 @@
{
"message": "Message (facultatif)",
"amount": "Montant ($)",
"send": "Envoyez"
}

View File

@@ -2,13 +2,13 @@
<v-container>
<v-row>
<v-text-field label="Message (facultatif)" v-model="tipMessage"
<v-text-field :label="t('message')" v-model="tipMessage"
style="border-radius: 10px; margin-top: 10px; margin-bottom: 10px; color: #a30e79; background-color: #f4f4f4">
</v-text-field>
</v-row>
<v-row>
<v-text-field label="Montant ($)" v-model="price"
<v-text-field :label="t('amount')" v-model="price"
style="border-radius: 10px; margin-bottom: 10px; color: #a30e79; background-color: #f4f4f4">
</v-text-field>
</v-row>
@@ -19,7 +19,7 @@
<v-icon left style="margin-right: 10px;">
mdi-gift
</v-icon>
Envoyez
{{ t('send') }}
</v-btn>
</v-row>
@@ -31,7 +31,7 @@
</div>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn block class="ma-auto" style="width: 200px;" text="Annuler" @click="closeDialog()"></v-btn>
<v-btn block class="ma-auto" style="width: 200px;" :text="t('cancel')" @click="closeDialog()"></v-btn>
</v-card-actions>
</v-card>
</template>
@@ -44,8 +44,10 @@
import { useClient } from '@/plugins/api.js';
import { loadStripe } from '@stripe/stripe-js';
import { onMounted, ref } from "vue";
import { useTranslations } from '@/translations/translations';
const props = defineProps(['creatorId'])
const t = useTranslations();
let stripe = null;
const client = useClient();

View File

@@ -0,0 +1,3 @@
{
"alt": "Creator banner"
}

View File

@@ -0,0 +1,3 @@
{
"alt": "Banner del creador"
}

View File

@@ -0,0 +1,3 @@
{
"alt": "Bannière du créateur"
}

View File

@@ -11,7 +11,7 @@
<img
class="w-[990px] h-[330px] banner object-cover"
:src="brandingStore.value?.images?.banner ?? '/images/placeholders/banner.png'"
alt="Profile Banner"
:alt="t('alt')"
>
<!-- Tint Effect -->
<div
@@ -43,9 +43,11 @@ import BannerEditor from "@/views/creators/BannerEditor.vue";
import {computed, ref} from "vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
import {useAuthStore} from "@/stores/authStore.js";
import { useTranslations } from '@/translations/translations';
const authStore = useAuthStore();
const brandingStore = useBrandingStore();
const t = useTranslations();
// State
const showTint = ref(false);

View File

@@ -0,0 +1,12 @@
{
"social": {
"facebook": "Facebook",
"instagram": "Instagram",
"linkedin": "LinkedIn",
"reddit": "Reddit",
"tiktok": "TikTok",
"x": "X (Twitter)",
"youtube": "YouTube",
"website": "Website"
}
}

View File

@@ -0,0 +1,12 @@
{
"social": {
"facebook": "Facebook",
"instagram": "Instagram",
"linkedin": "LinkedIn",
"reddit": "Reddit",
"tiktok": "TikTok",
"x": "X (Twitter)",
"youtube": "YouTube",
"website": "Sitio Web"
}
}

View File

@@ -0,0 +1,12 @@
{
"social": {
"facebook": "Facebook",
"instagram": "Instagram",
"linkedin": "LinkedIn",
"reddit": "Reddit",
"tiktok": "TikTok",
"x": "X (Twitter)",
"youtube": "YouTube",
"website": "Site Web"
}
}

View File

@@ -12,11 +12,11 @@ import Tiktok from "@/views/svg/Tiktok.vue";
import Reddit from "@/views/svg/Reddit.vue";
import Youtube from "@/views/svg/Youtube.vue";
import Web from "@/views/svg/Web.vue";
import { useTranslations } from '@/translations/translations'
const brandingStore = useBrandingStore();
const baseURL = window.location.origin;
const t = useTranslations();
// Gèrer le breakpoint du block information.
// Définir un point de rupture pour "moyen" (correspondant à md: de Tailwind)
@@ -88,49 +88,57 @@ onUnmounted(() => {
<a v-if="brandingStore.value?.socials?.facebookUrl"
:href="brandingStore.value?.socials?.facebookUrl"
target="_blank">
target="_blank"
:title="t('social.facebook')">
<facebook class="social-icon"></facebook>
</a>
<a v-if="brandingStore.value?.socials?.instagramUrl"
:href="brandingStore.value?.socials?.instagramUrl"
target="_blank">
target="_blank"
:title="t('social.instagram')">
<instagram class="social-icon"></instagram>
</a>
<a v-if="brandingStore.value?.socials?.linkedInUrl"
:href="brandingStore.value?.socials?.linkedInUrl"
target="_blank">
target="_blank"
:title="t('social.linkedin')">
<linkedin class="social-icon"></linkedin>
</a>
<a v-if="brandingStore.value?.socials?.redditUrl"
:href="brandingStore.value?.socials?.redditUrl"
target="_blank">
target="_blank"
:title="t('social.reddit')">
<reddit class="social-icon"></reddit>
</a>
<a v-if="brandingStore.value?.socials?.tikTokUrl"
:href="brandingStore.value?.socials?.tikTokUrl"
target="_blank">
target="_blank"
:title="t('social.tiktok')">
<tiktok class="social-icon"></tiktok>
</a>
<a v-if="brandingStore.value?.socials?.xUrl"
:href="brandingStore.value?.socials?.xUrl"
target="_blank">
target="_blank"
:title="t('social.x')">
<x class="social-icon"></x>
</a>
<a v-if="brandingStore.value?.socials?.youtubeUrl"
:href="brandingStore.value?.socials?.youtubeUrl"
target="_blank">
target="_blank"
:title="t('social.youtube')">
<youtube class="social-icon"></youtube>
</a>
<a v-if="brandingStore.value?.socials?.websiteUrl"
:href="brandingStore.value?.socials?.websiteUrl"
target="_blank">
target="_blank"
:title="t('social.website')">
<web class="social-icon"></web>
</a>

View File

@@ -0,0 +1,7 @@
{
"title": "Choose your Banner",
"description": "The banner must have a 3:1 ratio. Target dimensions are 960 x 320.",
"chooseImage": "Choose an image...",
"clickToEdit": "Click to edit",
"preview": "Banner preview"
}

View File

@@ -0,0 +1,7 @@
{
"title": "Elige tu Banner",
"description": "El banner debe tener una relación de 3:1. Las dimensiones objetivo son 960 x 320.",
"chooseImage": "Elegir una imagen...",
"clickToEdit": "Clic para editar",
"preview": "Vista previa del banner"
}

View File

@@ -0,0 +1,7 @@
{
"title": "Choisissez votre Bannière",
"description": "La bannière doit avoir un ratio de 3:1. Les dimensions cibles sont 960 x 320.",
"chooseImage": "Choisir une image...",
"clickToEdit": "Cliquez pour modifier",
"preview": "Aperçu de la bannière"
}

View File

@@ -1,12 +1,12 @@
<template>
<div class="card">
<div class="card-title">
Choisissez votre Bannière
{{ t('title') }}
</div>
<div class="card-content">
<p class="card-text">
La bannière doit avoir un ratio de 3:1. Les dimensions cibles sont 960 x 320.
{{ t('description') }}
</p>
<div class="file-input-container">
@@ -21,7 +21,7 @@
class="choose-file-button"
@click="triggerFileInput"
>
Choisir une image...
{{ t('chooseImage') }}
</button>
</div>
@@ -44,11 +44,11 @@
<div v-else class="image-preview-container" @click="startEditing">
<img
:src="fileUrl || fallbackUrl"
alt="Aperçu de la bannière"
:alt="t('preview')"
class="preview-image"
/>
<div class="edit-overlay">
<span class="edit-text">Cliquez pour modifier</span>
<span class="edit-text">{{ t('clickToEdit') }}</span>
</div>
</div>
</div>
@@ -56,12 +56,12 @@
<div class="card-actions">
<button class="secondary"
@click="cancel">
Annuler
{{ t('cancel') }}
</button>
<button class="primary"
@click="showCropper ? applyCrop() : publish()"
:disabled="!selectedFile">
{{ showCropper ? 'Appliquer' : 'Enregistrer' }}
{{ showCropper ? t('apply') : t('save') }}
</button>
</div>
</div>
@@ -72,6 +72,7 @@ import {ref} from 'vue'
import {useClient} from '@/plugins/api.js'
import { Cropper } from 'vue-advanced-cropper'
import 'vue-advanced-cropper/dist/style.css'
import { useTranslations } from '@/translations/translations'
const props = defineProps({
creator: {
@@ -92,6 +93,9 @@ const cropper = ref(null)
const TARGET_WIDTH = 960
const TARGET_HEIGHT = 320
// Get translations for this component
const t = useTranslations()
const triggerFileInput = () => {
fileInput.value.click()
}
@@ -126,7 +130,7 @@ const startEditing = () => {
})
.catch(error => {
console.error('Error loading image for editing:', error)
errorMessage.value = 'Une erreur est survenue lors du chargement de l\'image'
errorMessage.value = t('errors.imageLoad')
})
}
}
@@ -162,7 +166,7 @@ const publish = async () => {
emits('closeRequested')
} catch (error) {
console.error(error)
errorMessage.value = 'Une erreur est survenue lors de l\'envoi de l\'image'
errorMessage.value = t('errors.imageUpload')
}
}

View File

@@ -0,0 +1,3 @@
{
"title": "Create your Hutopy"
}

View File

@@ -0,0 +1,3 @@
{
"title": "Crea tu Hutopy"
}

View File

@@ -0,0 +1,3 @@
{
"title": "Créez votre Hutopy"
}

View File

@@ -5,6 +5,7 @@ import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useClient} from "@/plugins/api.js";
import {useRouter, useRoute} from "vue-router";
import NameEditor from "@/views/creators/NameEditor.vue";
import { useTranslations } from '@/translations/translations'
const creatorName = ref('');
const creatorNameReservationId = ref(undefined);
@@ -17,6 +18,7 @@ const router = useRouter();
const route = useRoute();
const creatorProfileStore = useCreatorProfileStore();
const userProfileStore = useUserProfileStore();
const t = useTranslations();
function handleCreatorNameReservationIdChanged($event) {
creatorNameReservationId.value = $event
@@ -48,9 +50,9 @@ async function createAccount() {
await router.push(`/@${creatorProfileStore.creator.slug}`);
} catch (error) {
if (error?.response?.data?.errors) {
errorMessage.value = error.response.data.errors[0]?.['reason'] || 'An unexpected error occurred.';
errorMessage.value = error.response.data.errors[0]?.['reason'] || t('errors.unexpected');
} else {
errorMessage.value = error?.response?.data?.message || error.message || 'An unexpected error occurred.';
errorMessage.value = error?.response?.data?.message || error.message || t('errors.unexpected');
}
} finally {
isOperationPending.value = false;
@@ -64,7 +66,7 @@ async function createAccount() {
<div class="card">
<div class="card-title">
Créez votre Hutopy.
{{ t('title') }}
</div>
<div class="card-content">
@@ -79,13 +81,13 @@ async function createAccount() {
<button
class="secondary"
@click="cancel">
Cancel
{{ t('cancel') }}
</button>
<button
class="primary"
:disabled="!canSave || isOperationPending"
@click="createAccount">
Créer
{{ t('create') }}
</button>
</div>

View File

@@ -0,0 +1,19 @@
{
"sections": {
"about": {
"title": "About Us",
"description": "Description",
"mainImage": "Main image"
},
"support": {
"title": "Why Support Us",
"description": "Description",
"subtitle": "Subtitle"
}
},
"fields": {
"phoneNumber": "Phone Number",
"email": "Email Address",
"videoUrl": "Video URL"
}
}

View File

@@ -0,0 +1,19 @@
{
"sections": {
"about": {
"title": "Quiénes Somos",
"description": "Descripción",
"mainImage": "Imagen principal"
},
"support": {
"title": "Por Qué Apoyarnos",
"description": "Descripción",
"subtitle": "Subtítulo"
}
},
"fields": {
"phoneNumber": "Número de Teléfono",
"email": "Dirección de Correo",
"videoUrl": "URL del Video"
}
}

View File

@@ -0,0 +1,19 @@
{
"sections": {
"about": {
"title": "Qui sommes-nous",
"description": "Description",
"mainImage": "Image principale"
},
"support": {
"title": "Pourquoi nous supporter",
"description": "Description",
"subtitle": "Sous-titre"
}
},
"fields": {
"phoneNumber": "Numéro de Téléphone",
"email": "Adresse Email",
"videoUrl": "URL Vidéo"
}
}

View File

@@ -9,7 +9,7 @@
class="primary"
@click="isEditMode ? saveChanges() : toggleEditMode()"
>
{{ isEditMode ? 'Enregistrer' : 'Éditer la page' }}
{{ isEditMode ? t('save') : t('edit') }}
</button>
<button
@@ -17,14 +17,14 @@
class="secondary"
@click="cancelEdit"
>
Annuler
{{ t('cancel') }}
</button>
</div>
<!-- MainPage -->
<div class="flex flex-col mt-4">
<h1 class="flex justify-start text-2xl font-bold text-center mb-4">Qui sommes-nous</h1>
<h1 class="flex justify-start text-2xl font-bold text-center mb-4">{{ t('sections.about.title') }}</h1>
<div>
<!-- Main image Bloc D'information-->
@@ -35,7 +35,7 @@
{{ mainImageText }}
</p>
</div>
<v-textarea v-if="isEditMode" v-model="editableMainImageText" class="w-full p-2 py-6 " label="Description"
<v-textarea v-if="isEditMode" v-model="editableMainImageText" class="w-full p-2 py-6 " :label="t('sections.about.description')"
variant="outlined"></v-textarea>
<div class="flex flex-row items-center space-x-4">
@@ -44,26 +44,26 @@
<img
v-if="mainImageUrl"
:src="mainImageUrl"
alt="Image principale"
:alt="t('sections.about.mainImage')"
class="max-w-full h-auto cursor-pointer"/>
</div>
<div v-if="isEditMode" class="relative flex justify-center">
<label>
<input class="hidden" type="file" @change="updateImage('mainImageUrl', $event)"/>
<img :src="mainImageUrl || fallbackImage"
alt="Image principale"
:alt="t('sections.about.mainImage')"
class=" max-w-full h-auto cursor-pointer max-h-96"/>
</label>
<button v-if="isEditMode"
class="absolute top-10 right-2 px-2 py-1 bg-red-500 text-white hover:bg-red-600"
@click="deleteImage('mainImageUrl')">
X
{{ t('delete') }}
</button>
</div>
<div class="w-1/2 flex flex-col justify-center">
<h2 v-if="videoSubtitleMain" class="text-xl font-semibold text-center">
Pourquoi nous supporter
{{ t('sections.support.title') }}
</h2>
<div v-if="!isEditMode">
@@ -76,7 +76,7 @@
<v-textarea
v-model="editableMainVideoText"
class="p-2 rounded-md mt-4"
label="Description"
:label="t('sections.support.description')"
rows="10"
variant="outlined"
></v-textarea>
@@ -92,23 +92,17 @@
<v-text-field
v-model="editableVideoSubtitle"
class="w-full p-2"
label="Titre"
:label="t('sections.support.subtitle')"
variant="outlined"
></v-text-field>
</div>
<v-textarea v-if="isEditMode"
v-model="editableImagesText"
class="w-full p-2 border rounded-md"
rows="10"
variant="outlined">
</v-textarea>
<div v-if="!isEditMode">
<p v-if="imagesText" class="text-lg text-justify">
{{ imagesText }}
</p>
</div>
v-model="editableVideoText"
class="w-full p-2"
:label="t('sections.support.description')"
variant="outlined"
></v-textarea>
</div>
</div>
@@ -127,10 +121,9 @@
<div v-if="isEditMode">
<v-text-field
v-if="isEditMode"
v-model="editableVideoUrlMain"
class="w-full p-2 rounded-md"
label="URL Video"
:label="t('fields.videoUrl')"
type="text"
variant="outlined"
/>
@@ -248,14 +241,14 @@
<v-text-field
v-model="editablePhoneNumber"
class="w-full p-2"
label="Numéro de Téléphone"
:label="t('fields.phoneNumber')"
variant="outlined"
></v-text-field>
<v-text-field
v-model="editableEmail"
class="w-full p-2"
label="Adresse Email"
:label="t('fields.email')"
variant="outlined"
></v-text-field>
</div>
@@ -290,9 +283,13 @@ import {onMounted, ref} from "vue";
import {useClient} from "@/plugins/api.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useAuthStore} from "@/stores/authStore.js";
import {useTranslations} from '@/translations/translations'
const creatorProfileStore = useCreatorProfileStore();
const brandingStore = useBrandingStore();
const authStore = useAuthStore();
const t = useTranslations();
const client = useClient();
const isLoading = ref(true);

View File

@@ -0,0 +1,5 @@
{
"deletion": {
"pending": "This Creator page is pending deletion. You can revert the action in your profile."
}
}

View File

@@ -0,0 +1,5 @@
{
"deletion": {
"pending": "Esta página de creador está pendiente de eliminación. Puede revertir la acción en su perfil."
}
}

View File

@@ -0,0 +1,5 @@
{
"deletion": {
"pending": "Cette page de créateur est en attente de suppression. Vous pouvez annuler l'action dans votre profil."
}
}

View File

@@ -3,9 +3,11 @@ import {useBrandingStore} from "@/stores/brandingStore.js";
import {onMounted} from "vue";
import Banner from "@/views/creators/Banner.vue";
import Footer from "@/views/main/Footer.vue";
import { useTranslations } from '@/translations/translations'
const brandingStore = useBrandingStore();
const creatorName = window.location.pathname.split('/@').pop();
const t = useTranslations();
onMounted(async () => {
await brandingStore.updateBrand(creatorName);
@@ -23,7 +25,7 @@ onMounted(async () => {
<div v-else>
<div v-if="brandingStore.value.isDeleted"
class="bg-red-500 p-2 m-4 text-center font-semibold">
This Creator page is pending deletion. You can revert the action in your profile.
{{ t('deletion.pending') }}
</div>
<banner></banner>
<router-view></router-view>

View File

@@ -0,0 +1,4 @@
{
"alt": "Profile Picture",
"edit": "Edit Profile Picture"
}

View File

@@ -0,0 +1,4 @@
{
"alt": "Foto de perfil",
"edit": "Editar foto de perfil"
}

View File

@@ -0,0 +1,4 @@
{
"alt": "Photo de profil",
"edit": "Modifier la photo de profil"
}

View File

@@ -8,13 +8,14 @@
<img
class="shadow-2xl object-cover rounded-full border-solid border-hSecondary border-102 w-[200px] h-[200px] logo-image"
:src="brandingStore.value.images?.logo ?? '/images/placeholders/profile.png'"
alt="Profile Picture"
:alt="t('alt')"
/>
<!-- Tint Effect -->
<div
v-if="showTint"
class="absolute rounded-full inset-0 bg-black/25 cursor-pointer"
:title="t('edit')"
>
<!-- Top-right Icon -->
<div
@@ -42,9 +43,11 @@ import {useAuthStore} from "@/stores/authStore.js";
import {useBrandingStore} from "@/stores/brandingStore.js";
import CreatorLogoEditor from "@/views/creators/CreatorLogoEditor.vue";
import {computed, ref} from "vue";
import { useTranslations } from '@/translations/translations'
const authStore = useAuthStore();
const brandingStore = useBrandingStore();
const t = useTranslations();
// State
const showTint = ref(false);

View File

@@ -0,0 +1,7 @@
{
"title": "Choose your Logo",
"description": "The logo must be square. Recommended dimensions are 200 x 200 pixels.",
"chooseImage": "Choose an image...",
"clickToEdit": "Click to edit",
"preview": "Logo preview"
}

View File

@@ -0,0 +1,7 @@
{
"title": "Elige tu Logo",
"description": "El logo debe ser cuadrado. Las dimensiones recomendadas son 200 x 200 píxeles.",
"chooseImage": "Elegir una imagen...",
"clickToEdit": "Clic para editar",
"preview": "Vista previa del logo"
}

View File

@@ -0,0 +1,7 @@
{
"title": "Choisissez votre Logo",
"description": "Le logo doit être carré. Les dimensions recommandées sont 200 x 200 pixels.",
"chooseImage": "Choisir une image...",
"clickToEdit": "Cliquez pour modifier",
"preview": "Aperçu du logo"
}

View File

@@ -1,12 +1,12 @@
<template>
<div class="card">
<div class="card-title">
Choisissez votre Logo
{{ t('title') }}
</div>
<div class="card-content">
<p class="card-text">
Le logo doit être carré. Les dimensions recommandées sont 200 x 200 pixels.
{{ t('description') }}
</p>
<div class="file-input-container">
@@ -21,7 +21,7 @@
class="choose-file-button"
@click="triggerFileInput"
>
Choisir une image...
{{ t('chooseImage') }}
</button>
</div>
@@ -46,11 +46,11 @@
<div class="circular-preview">
<img
:src="fileUrl || fallbackUrl"
alt="Aperçu du logo"
:alt="t('preview')"
class="preview-image"
/>
<div class="edit-overlay">
<span class="edit-text">Cliquez pour modifier</span>
<span class="edit-text">{{ t('clickToEdit') }}</span>
</div>
</div>
</div>
@@ -59,12 +59,12 @@
<div class="card-actions">
<button class="secondary"
@click="cancel">
Annuler
{{ t('cancel') }}
</button>
<button class="primary"
@click="showCropper ? applyCrop() : publish()"
:disabled="!selectedFile">
{{ showCropper ? 'Appliquer' : 'Enregistrer' }}
{{ showCropper ? t('apply') : t('save') }}
</button>
</div>
</div>
@@ -75,6 +75,7 @@ import {ref} from 'vue'
import {useClient} from '@/plugins/api.js'
import { Cropper, CircleStencil } from 'vue-advanced-cropper'
import 'vue-advanced-cropper/dist/style.css'
import { useTranslations } from '@/translations/translations'
const props = defineProps({
creator: {
@@ -95,6 +96,9 @@ const cropper = ref(null)
const TARGET_WIDTH = 200
const TARGET_HEIGHT = 200
// Get translations for this component
const t = useTranslations()
const triggerFileInput = () => {
fileInput.value.click()
}
@@ -129,7 +133,7 @@ const startEditing = () => {
})
.catch(error => {
console.error('Error loading image for editing:', error)
errorMessage.value = 'Une erreur est survenue lors du chargement de l\'image'
errorMessage.value = t('errors.imageLoad')
})
}
}
@@ -165,7 +169,7 @@ const publish = async () => {
emits('closeRequested')
} catch (error) {
console.error(error)
errorMessage.value = 'Une erreur est survenue lors de l\'envoi de l\'image'
errorMessage.value = t('errors.imageUpload')
}
}

View File

@@ -0,0 +1,10 @@
{
"isupport": "I Support",
"amount": "Amount",
"message": "Message",
"send": "Send",
"cancel": "Cancel",
"errors": {
"payment": "An error occurred during payment processing"
}
}

View File

@@ -0,0 +1,10 @@
{
"isupport": "Apoyo",
"amount": "Cantidad",
"message": "Mensaje",
"send": "Enviar",
"cancel": "Cancelar",
"errors": {
"payment": "Ocurrió un error durante el procesamiento del pago"
}
}

View File

@@ -0,0 +1,10 @@
{
"isupport": "Je Soutiens",
"amount": "Montant",
"message": "Message",
"send": "Envoyer",
"cancel": "Annuler",
"errors": {
"payment": "Une erreur s'est produite lors du traitement du paiement"
}
}

View File

@@ -3,13 +3,13 @@
class="secondary donation-action"
@click="openDonationDialog()"
>
{{ $t('isupportbtn.isupport') }}
{{ t('isupport') }}
</button>
<v-dialog v-model="donationModal">
<div class="card">
<div class="card-title">
{{ $t('isupportbtn.isupport') }}
{{ t('isupport') }}
</div>
<div class="card-content">
@@ -20,7 +20,7 @@
placeholder="0"
:min="0"
class="p-2"
:label="`${$t('isupportbtn.amount')}`"
:label="t('amount')"
density="comfortable"
variant="outlined"
hide-details
@@ -32,7 +32,7 @@
<v-textarea
v-model="tipMessage"
:label="`${$t('isupportbtn.message')}`"
:label="t('message')"
class="p-2"
density="comfortable"
variant="outlined"
@@ -45,12 +45,12 @@
<button class="secondary"
@click="closeDonationDialog()">
Cancel
{{ t('cancel') }}
</button>
<button class="primary"
@click="goPay()">
{{ $t('isupportbtn.send') }}
{{ t('send') }}
</button>
</div>
@@ -71,7 +71,7 @@
class="ma-auto"
style="width: 200px"
@click="closeDialog()"
>Annuler
>{{ t('cancel') }}
</v-btn>
</v-card-actions>
</v-card>
@@ -84,8 +84,10 @@ import {useClient} from '@/plugins/api.js';
import {useBrandingStore} from '@/stores/brandingStore.js';
import {loadStripe} from '@stripe/stripe-js';
import {onMounted, ref} from 'vue';
import { useTranslations } from '@/translations/translations'
const brandingStore = useBrandingStore();
const t = useTranslations();
const props = defineProps({
creatorId: {default: 'missing-creator-id', required: true},
@@ -134,7 +136,7 @@ async function createCheckoutSession() {
return clientSecret.data;
} catch (error) {
console.error(error);
errorMessage.value = 'Une erreur est survenue. Veuillez réessayer.';
errorMessage.value = t('errors.payment');
}
}

View File

@@ -0,0 +1,3 @@
{
"label": "Page name"
}

View File

@@ -0,0 +1,3 @@
{
"label": "Nombre de la página"
}

View File

@@ -0,0 +1,4 @@
{
"label": "Nom de la page"
}
}

View File

@@ -2,6 +2,7 @@
import {ref, onMounted, onUnmounted} from "vue";
import {v7} from "uuid";
import {useClient} from "@/plugins/api.js";
import { useTranslations } from '@/translations/translations'
const props = defineProps({
name: {
@@ -18,6 +19,7 @@ const emits = defineEmits([
]);
const name = ref(props.name);
const t = useTranslations();
const isOperationPending = ref(false);
const reservationState = ref(null);
@@ -110,7 +112,7 @@ onUnmounted(() => {
<template>
<v-text-field
variant="outlined"
label="Nom de la page"
:label="t('label')"
v-model="name"
outlined
@input="handleInput"

View File

@@ -0,0 +1,3 @@
{
"verified": "Verified Account"
}

View File

@@ -0,0 +1,3 @@
{
"verified": "Cuenta Verificada"
}

View File

@@ -0,0 +1,3 @@
{
"verified": "Compte Vérifié"
}

View File

@@ -1,7 +1,8 @@
<template>
<div v-show="brandingStore.value.verified"
class="text-blue m-4">
class="text-blue m-4"
:title="t('verified')">
<icon-account-verified></icon-account-verified>
</div>
@@ -20,5 +21,8 @@
<script setup>
import IconAccountVerified from "@/components/icons/IconAccountVerified.vue";
import {useBrandingStore} from "@/stores/brandingStore.js";
import { useTranslations } from '@/translations/translations'
const brandingStore = useBrandingStore();
const t = useTranslations();
</script>

View File

@@ -0,0 +1,10 @@
{
"helpandcontact": "Help & Contact",
"faq": "FAQ",
"creatorguide": "Creator Guide",
"termsandconditions": "Terms & Conditions",
"contentpolicy": "Content Policy",
"about": "About",
"pricing": "Pricing",
"allRightsReserved": "All Rights Reserved"
}

View File

@@ -0,0 +1,10 @@
{
"helpandcontact": "Ayuda y Contacto",
"faq": "FAQ",
"creatorguide": "Guía del Creador",
"termsandconditions": "Términos y Condiciones",
"contentpolicy": "Política de Contenido",
"about": "Acerca de",
"pricing": "Precios",
"allRightsReserved": "Todos los Derechos Reservados"
}

View File

@@ -0,0 +1,10 @@
{
"helpandcontact": "Aide & Contact",
"faq": "FAQ",
"creatorguide": "Guide du Créateur",
"termsandconditions": "Conditions Générales",
"contentpolicy": "Politique de Contenu",
"about": "À Propos",
"pricing": "Tarifs",
"allRightsReserved": "Tous Droits Réservés"
}

View File

@@ -2,6 +2,9 @@
import Instagram from "@/views/svg/Instagram.vue";
import Facebook from "@/views/svg/Facebook.vue";
import X from "@/views/svg/X.vue";
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
</script>
<template>
@@ -23,36 +26,36 @@ import X from "@/views/svg/X.vue";
<div class="footer-links">
<router-link to="/documents/helpandcontact"
class="link">
{{ $t('footer.helpandcontact') }}
{{ t('helpandcontact') }}
</router-link>
<router-link to="/documents/faq"
class="link">
{{ $t('footer.faq') }}
{{ t('faq') }}
</router-link>
<router-link to="/documents/guideforcreators"
class="link">
{{ $t('footer.creatorguide') }}
{{ t('creatorguide') }}
</router-link>
<router-link to="/documents/termsandconditions"
class="link">
{{ $t('footer.termsandconditions') }}
{{ t('termsandconditions') }}
</router-link>
<router-link to="/documents/contentpolicy"
class="link">
{{ $t('footer.contentpolicy') }}
{{ t('contentpolicy') }}
</router-link>
<router-link to="/documents/about"
class="link">
{{ $t('footer.about') }}
{{ t('about') }}
</router-link>
<router-link to="/documents/pricing"
class="link">
{{ $t('footer.pricing') }}
{{ t('pricing') }}
</router-link>
</div>
<div class="footer-copyright">
Hutopy &copy;{{ new Date().getFullYear() }} - {{ $t('footer.allRightsReserved') }}
Hutopy &copy;{{ new Date().getFullYear() }} - {{ t('allRightsReserved') }}
</div>
</footer>

View File

@@ -0,0 +1,7 @@
{
"myPage": "my page",
"myProfile": "my profile",
"reduce": "collapse",
"signIn": "login",
"signOut": "sign out"
}

View File

@@ -0,0 +1,7 @@
{
"myPage": "mi página",
"myProfile": "mi perfil",
"reduce": "reducir",
"signIn": "iniciar sesión",
"signOut": "cerrar sesión"
}

View File

@@ -0,0 +1,7 @@
{
"myPage": "ma page",
"myProfile": "mon profil",
"reduce": "réduire",
"signIn": "connexion",
"signOut": "se déconnecter"
}

View File

@@ -1,35 +1,24 @@
<script setup>
import {computed} from "vue";
import {useI18n} from "vue-i18n";
import {useAuthStore} from "@/stores/authStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
import { useTranslations } from '@/translations/translations'
import {useLanguageStore} from "@/stores/languageStore.js";
const {locale} = useI18n();
const translations = {
en: {
myPage: "my page",
myProfile: "my profile",
reduce: "collapse",
signIn: "login",
signOut: "sign out"
},
fr: {
myPage: "ma page",
myProfile: "mon profil",
reduce: "réduire",
signIn: "connexion",
signOut: "se déconnecter"
}
};
const t = computed(() => translations[locale.value] || translations["en"]);
const t = useTranslations();
const languageStore = useLanguageStore();
const userProfileStore = useUserProfileStore();
const creatorProfileStore = useCreatorProfileStore();
const authStore = useAuthStore();
function toggleLanguage() {
locale.value = locale.value === 'fr' ? 'en' : 'fr';
const languages = ['fr', 'en', 'es'];
const currentIndex = languages.indexOf(locale.value);
const nextIndex = (currentIndex + 1) % languages.length;
languageStore.setLocale(languages[nextIndex]);
}
</script>
@@ -73,13 +62,13 @@ function toggleLanguage() {
<router-link v-if="creatorProfileStore.hasCreator" :to="`/@${creatorProfileStore.creator.slug}`">
<button class="menu-item-action">
<i class="mdi mdi-file-account-outline"></i>
<span class="label">{{ t.myPage }}</span>
<span class="label">{{ t('myPage') }}</span>
</button>
</router-link>
<router-link v-else to="/create-creator">
<button class="menu-item-action">
<i class="mdi mdi-file-account-outline"></i>
<span class="label">{{ t.myPage }}</span>
<span class="label">{{ t('myPage') }}</span>
</button>
</router-link>
</template>
@@ -88,7 +77,7 @@ function toggleLanguage() {
<router-link to="/profile">
<button class="menu-item-action">
<i class="mdi mdi-account"></i>
<span class="label">{{ t.myProfile }}</span>
<span class="label">{{ t('myProfile') }}</span>
</button>
</router-link>
</template>
@@ -103,7 +92,7 @@ function toggleLanguage() {
<router-link to="/login">
<button class="menu-item-action">
<i class="mdi mdi-login"></i>
<span class="label">{{ t.signIn }}</span>
<span class="label">{{ t('signIn') }}</span>
</button>
</router-link>
</template>
@@ -111,7 +100,7 @@ function toggleLanguage() {
<button class="menu-item-action"
@click="authStore.logout">
<i class="mdi mdi-logout"></i>
<span class="label">{{ t.signOut }}</span>
<span class="label">{{ t('signOut') }}</span>
</button>
</div>
@@ -163,10 +152,10 @@ function toggleLanguage() {
}
.menu-item-action {
/* FIXME: The hover value is not semantically correct */
@apply bg-hBackground hover:bg-hSurface;
@apply capitalize;
@apply flex items-center gap-4 py-2 rounded;
/* FIXME: The hover value is not semantically correct */
@apply mx-2 lg:mx-0;
@apply lg:px-4;
@apply w-10 h-10 justify-center lg:w-full lg:h-auto lg:justify-normal;

View File

@@ -0,0 +1,22 @@
{
"personal": {
"informations": "Personal Information",
"fullname": "Full Name",
"alias": "Alias",
"contact": "Contact Details",
"email": "Email"
},
"creator": {
"informations": "Creator Information",
"name": "Name",
"slug": "URL",
"title": "Title",
"socialnetwork": "Social Networks"
},
"danger": {
"title": "Danger Zone",
"warning": "CAUTION: This will delete your creator page and suspend all tips and donations.",
"delete": "DELETE PAGE",
"restore": "RESTORE PAGE"
}
}

View File

@@ -0,0 +1,22 @@
{
"personal": {
"informations": "Información personal",
"fullname": "Nombre completo",
"alias": "Alias",
"contact": "Detalles de contacto",
"email": "Correo electrónico"
},
"creator": {
"informations": "Información del creador",
"name": "Nombre",
"slug": "URL",
"title": "Título",
"socialnetwork": "Redes sociales"
},
"danger": {
"title": "Zona de peligro",
"warning": "PRECAUCIÓN: Esto eliminará tu página de creador y suspenderá todos los propinas y donaciones.",
"delete": "ELIMINAR PÁGINA",
"restore": "RESTAURAR PÁGINA"
}
}

View File

@@ -0,0 +1,22 @@
{
"personal": {
"informations": "Informations personnelles",
"fullname": "Nom complet",
"alias": "Pseudonyme",
"contact": "Coordonnées",
"email": "Email"
},
"creator": {
"informations": "Informations du créateur",
"name": "Nom",
"slug": "URL",
"title": "Titre",
"socialnetwork": "Réseaux sociaux"
},
"danger": {
"title": "Zone de danger",
"warning": "ATTENTION : Cela supprimera votre page de créateur et suspendra tous les pourboires et dons.",
"delete": "SUPPRIMER LA PAGE",
"restore": "RESTAURER LA PAGE"
}
}

View File

@@ -18,7 +18,9 @@ import Linkedin from "@/views/svg/Linkedin.vue";
import Tiktok from "@/views/svg/Tiktok.vue";
import Instagram from "@/views/svg/Instagram.vue";
import Facebook from "@/views/svg/Facebook.vue";
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const userProfileStore = useUserProfileStore()
// ### Fullname
@@ -131,14 +133,14 @@ const closeDialog = () => {
<div class="card">
<div class="card-title">
{{ $t('personnalinformation.informations') }}
{{ t('personal.informations') }}
</div>
<div class="content">
<button
class="action"
@click="openEditFullname">
<span class="label">{{ $t('personnalinformation.fullname') }}</span>
<span class="label">{{ t('personal.fullname') }}</span>
<span class="value">{{ userProfileStore.fullname }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -146,7 +148,7 @@ const closeDialog = () => {
<button
class="action"
@click="openEditAlias">
<span class="label">{{ $t('personnalinformation.alias') }}</span>
<span class="label">{{ t('personal.alias') }}</span>
<span class="value">{{ userProfileStore.user.alias }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -157,12 +159,12 @@ const closeDialog = () => {
<div class="card">
<div class="card-title">
{{ $t('personnalinformation.contactdetails') }}
{{ t('personal.contact') }}
</div>
<div class="content">
<button class="action" @click="openDialog('EmailDialog')">
<span class="label">{{ $t('personnalinformation.email') }}</span>
<span class="label">{{ t('personal.email') }}</span>
<span class="value">{{ userProfileStore.user.email }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -173,26 +175,26 @@ const closeDialog = () => {
<template v-if="creatorProfileStore.creator !== undefined">
<div class="card">
<div class="card-title">
{{ $t('creatorinfopage.informations') }}
{{ t('creator.informations') }}
</div>
<div class="content">
<!-- NAME -->
<button class="action" @click="openDialog('ChangeNameDialog')">
<span class="label">{{ $t('creatorinfopage.name') }}</span>
<span class="label">{{ t('creator.name') }}</span>
<span class="value">{{ creatorProfileStore.creator.name }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
<button class="action" @click="openDialog('ChangeSlugDialog')">
<span class="label">{{ $t('creatorinfopage.slug') }}</span>
<span class="label">{{ t('creator.slug') }}</span>
<span class="value">@{{ creatorProfileStore.creator.slug }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
<!-- TITLE -->
<button class="action" @click="openDialog('ChangeTitleDialog')">
<span class="label">{{ $t('creatorinfopage.title') }}</span>
<span class="label">{{ t('creator.title') }}</span>
<span class="value">{{ creatorProfileStore.creator.title }}</span>
<span class="chevron"><v-icon>mdi-chevron-right</v-icon></span>
</button>
@@ -209,7 +211,7 @@ const closeDialog = () => {
<div class="card">
<div class="card-title">
{{ $t('creatorinfopage.socialnetwork') }}
{{ t('creator.socialnetwork') }}
</div>
<div>
@@ -282,20 +284,20 @@ const closeDialog = () => {
</div>
<div class="card">
<div class="card-title">Danger Zone</div>
<div class="card-title">{{ t('danger.title') }}</div>
<div class="content">
<p>
CAUTION: This will delete your creator page and suspend all tips and donations.
{{ t('danger.warning') }}
</p>
<button v-if="!creatorProfileStore.creator.isDeleted"
class="danger-action"
@click="creatorProfileStore.removeCreatorPage()">
DELETE PAGE
{{ t('danger.delete') }}
</button>
<button v-else
class="safe-action"
@click="creatorProfileStore.restoreCreatorPage()">
RESTORE PAGE
{{ t('danger.restore') }}
</button>
</div>
</div>
@@ -307,12 +309,38 @@ const closeDialog = () => {
<style scoped>
.card {
@apply bg-hBackground rounded-lg p-4 w-full max-w-2xl;
}
.card-title {
@apply text-hOnBackground text-lg font-bold mb-4;
}
.content {
@apply flex flex-col gap-2;
}
.action {
@apply p-2 flex items-center w-full mb-2;
@apply font-sans text-base;
@apply rounded-md;
@apply transition duration-200 ease-in-out;
@apply hover:bg-hutopyPrimary active:bg-hutopySecondary;
@apply flex flex-row items-center justify-between w-full p-2 rounded-lg;
@apply hover:bg-hSurface;
}
.label {
@apply text-hOnBackground;
}
.value {
@apply text-hOnBackground opacity-70;
}
.chevron {
@apply text-hOnBackground opacity-70;
}
.social-icon {
@apply fill-current w-6 h-6;
@apply text-hOnBackground;
}
.danger-action {
@@ -326,22 +354,4 @@ const closeDialog = () => {
@apply mt-4;
@apply bg-green-800 hover:bg-green-700 active:bg-green-600;
}
.label {
@apply flex-none min-w-40 text-left;
}
.social-icon {
@apply w-6 h-6;
}
.value {
@apply flex-auto text-left pr-6;
@apply break-words overflow-auto;
}
.chevron {
@apply flex-none;
}
</style>

View File

@@ -0,0 +1,4 @@
{
"title": "Address",
"label": "Your address"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Dirección",
"label": "Tu dirección"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Adresse",
"label": "Votre adresse"
}

View File

@@ -1,31 +1,33 @@
<template>
<v-card>
<v-card-title>
Adresse
</v-card-title>
<div class="card dialog">
<div class="card-title">
{{ t('title') }}
</div>
<div class="m-4">
<div class="card-content">
<v-text-field
variant="outlined"
v-model="address"
label="Votre adresse"
:label="t('label')"
></v-text-field>
</div>
<v-card-actions>
<v-btn variant="plain" @click="requestClose">
Annuler
</v-btn>
<v-btn color="#A6147D" @click="requestSave">
Enregistrer
</v-btn>
</v-card-actions>
</v-card>
<div class="card-actions">
<button class="secondary" @click="requestClose">
{{ t('cancel') }}
</button>
<button class="primary" @click="requestSave">
{{ t('save') }}
</button>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['address'])
const emit = defineEmits(['close', 'save'])

View File

@@ -0,0 +1,4 @@
{
"title": "Alias",
"label": "Your alias"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Alias",
"label": "Tu alias"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Pseudonyme",
"label": "Votre pseudonyme"
}

View File

@@ -3,14 +3,14 @@
<div class="card dialog">
<div class="card-title">
{{ $t('personnalinformation.alias') }}
{{ t('title') }}
</div>
<div class="card-content">
<v-text-field
variant="outlined"
v-model="alias"
:label="$t('personnalinformation.alias')"
:label="t('label')"
></v-text-field>
</div>
@@ -18,12 +18,12 @@
<button class="secondary"
@click="requestClose">
Annuler
{{ t('cancel') }}
</button>
<button class="primary"
@click="requestSave">
Enregistrer
{{ t('save') }}
</button>
</div>
@@ -34,7 +34,9 @@
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['alias'])
const emit = defineEmits(['close', 'save'])

View File

@@ -0,0 +1,4 @@
{
"title": "Birthday",
"label": "Your birthday"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Fecha de nacimiento",
"label": "Tu fecha de nacimiento"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Date de naissance",
"label": "Votre date de naissance"
}

View File

@@ -1,31 +1,33 @@
<template>
<v-card>
<v-card-title>
Date de naissance
</v-card-title>
<div class="card dialog">
<div class="card-title">
{{ t('title') }}
</div>
<div class="m-4">
<div class="card-content">
<v-text-field
variant="outlined"
v-model="birthDate"
label="AAAA-MM-JJ"
:label="t('label')"
></v-text-field>
</div>
<v-card-actions>
<v-btn variant="plain" @click="requestClose">
Annuler
</v-btn>
<v-btn color="#A6147D" @click="requestSave">
Enregistrer
</v-btn>
</v-card-actions>
</v-card>
<div class="card-actions">
<button class="secondary" @click="requestClose">
{{ t('cancel') }}
</button>
<button class="primary" @click="requestSave">
{{ t('save') }}
</button>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['birthDate'])
const emit = defineEmits(['close', 'save'])

View File

@@ -0,0 +1,4 @@
{
"title": "Email",
"label": "Your email"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Correo electrónico",
"label": "Tu correo electrónico"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Email",
"label": "Votre email"
}

View File

@@ -1,13 +1,13 @@
<template>
<div class="card dialog">
<div class="card-title">
Courriel
{{ t('title') }}
</div>
<div class="card-content">
<v-text-field
v-model="email"
label="Votre courriel"
:label="t('label')"
variant="outlined"
></v-text-field>
</div>
@@ -15,11 +15,11 @@
<div class="card-actions">
<button class="secondary"
@click="cancel">
Annuler
{{ t('cancel') }}
</button>
<button class="primary"
@click="save">
Enregistrer
{{ t('save') }}
</button>
</div>
</div>
@@ -28,7 +28,9 @@
<script setup>
import {ref} from 'vue';
import {useClient} from "@/plugins/api.js";
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps({
creator: {
required: true

View File

@@ -0,0 +1,5 @@
{
"title": "Full Name",
"firstname": "First Name",
"lastname": "Last Name"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Nombre completo",
"firstname": "Nombre",
"lastname": "Apellido"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Nom complet",
"firstname": "Prénom",
"lastname": "Nom"
}

View File

@@ -1,6 +1,8 @@
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['firstname', 'lastname'])
const emit = defineEmits(['close', 'save'])
@@ -12,18 +14,16 @@ const requestSave = () => emit('save', firstname.value, lastname.value)
</script>
<template>
<div class="card dialog">
<div class="card-title">
{{ $t('personnalinformation.fullname') }}
<div class="card-title">
{{ t('title') }}
</div>
<div class="card-content">
<v-text-field
variant="outlined"
v-model="firstname"
:label="$t('personnalinformation.firstname')"
:label="t('firstname')"
></v-text-field>
</div>
@@ -31,23 +31,20 @@ const requestSave = () => emit('save', firstname.value, lastname.value)
<v-text-field
variant="outlined"
v-model="lastname"
:label="$t('personnalinformation.lastname')"
:label="t('lastname')"
></v-text-field>
</div>
<div class="card-actions">
<button class="secondary"
@click="requestClose">
Annuler
{{ t('cancel') }}
</button>
<button class="primary"
@click="requestSave">
Enregistrer
{{ t('save') }}
</button>
</div>
</div>
</template>

View File

@@ -0,0 +1,4 @@
{
"title": "Phone Number",
"label": "Your phone number"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Número de teléfono",
"label": "Tu número de teléfono"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Numéro de téléphone",
"label": "Votre numéro de téléphone"
}

View File

@@ -1,32 +1,33 @@
<template>
<v-card>
<v-card-title>
Numéro de téléphone
</v-card-title>
<div class="card dialog">
<div class="card-title">
{{ t('title') }}
</div>
<div class="m-4">
<div class="card-content">
<v-text-field
variant="outlined"
v-model="phone"
label="Votre numéro de téléphone"
:label="t('label')"
></v-text-field>
</div>
<v-card-actions>
<v-btn variant="plain" @click="requestClose">
Annuler
</v-btn>
<v-btn color="#A6147D" @click="requestSave">
Enregistrer
</v-btn>
</v-card-actions>
</v-card>
<div class="card-actions">
<button class="secondary" @click="requestClose">
{{ t('cancel') }}
</button>
<button class="primary" @click="requestSave">
{{ t('save') }}
</button>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['phone'])
const emit = defineEmits(['close', 'save'])

View File

@@ -0,0 +1,5 @@
{
"title": "Portrait",
"label": "Select an image",
"preview": "Portrait preview"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Retrato",
"label": "Selecciona una imagen",
"preview": "Vista previa del retrato"
}

View File

@@ -0,0 +1,5 @@
{
"title": "Portrait",
"label": "Sélectionnez une image",
"preview": "Aperçu du portrait"
}

View File

@@ -1,14 +1,14 @@
<template>
<v-card>
<v-card-title>
Portrait
</v-card-title>
<div class="card dialog">
<div class="card-title">
{{ t('title') }}
</div>
<div class="m-4">
<div class="card-content">
<img
:src="portraitData"
class="mb-5 w-full transition duration-200 ease-in-out transform"
alt="Aperçu de la bannière"
:alt="t('preview')"
/>
<v-file-input
@@ -16,25 +16,27 @@
v-model="selectedFile"
variant="outlined"
accept="image/*"
label="Votre bannière"
:label="t('label')"
></v-file-input>
</div>
<v-card-actions>
<v-btn variant="plain" @click="requestClose">
Annuler
</v-btn>
<v-btn color="#A6147D" @click="requestSave">
Enregistrer
</v-btn>
</v-card-actions>
</v-card>
<div class="card-actions">
<button class="secondary" @click="requestClose">
{{ t('cancel') }}
</button>
<button class="primary" @click="requestSave">
{{ t('save') }}
</button>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue';
import { useTranslations } from "@/translations/translations";
const t = useTranslations();
const props = defineProps(['portraitUrl'])
const emit = defineEmits(['close', 'save'])

Some files were not shown because too many files have changed in this diff Show More