feat(i18n): deprecate Spanish language support in the frontend
This commit is contained in:
@@ -1,246 +1,239 @@
|
||||
<template>
|
||||
<div class="card dialog">
|
||||
<div class="card-title">{{ t('changePhoneNumber') }}</div>
|
||||
<div class="card-content">
|
||||
<v-text-field
|
||||
v-model="displayPhoneNumber"
|
||||
class="w-full p-2"
|
||||
:label="t('phoneNumber')"
|
||||
type="tel"
|
||||
variant="outlined"
|
||||
:error-messages="phoneErrors"
|
||||
:rules="phoneRules"
|
||||
validate-on="blur"
|
||||
:placeholder="t('phonePlaceholder')"
|
||||
@input="handlePhoneInput"
|
||||
@keydown="handleKeydown"
|
||||
maxlength="14"
|
||||
/>
|
||||
|
||||
<v-alert
|
||||
v-if="!!errorMessage"
|
||||
outlined
|
||||
type="error"
|
||||
class="mt-4">
|
||||
{{ errorMessage }}
|
||||
</v-alert>
|
||||
<div class="card dialog">
|
||||
<div class="card-title">{{ t('changePhoneNumber') }}</div>
|
||||
<div class="card-content">
|
||||
<v-text-field
|
||||
v-model="displayPhoneNumber"
|
||||
:error-messages="phoneErrors"
|
||||
:label="t('phoneNumber')"
|
||||
:placeholder="t('phonePlaceholder')"
|
||||
:rules="phoneRules"
|
||||
class="w-full p-2"
|
||||
maxlength="14"
|
||||
type="tel"
|
||||
validate-on="blur"
|
||||
variant="outlined"
|
||||
@input="handlePhoneInput"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
|
||||
<div class="card-actions">
|
||||
<button class="secondary" @click="$emit('closeRequested')">
|
||||
{{ t('cancel') }}
|
||||
</button>
|
||||
<button class="primary" @click="savePhoneNumber" :disabled="!canSave || isLoading">
|
||||
{{ t('save') }}
|
||||
</button>
|
||||
</div>
|
||||
<v-alert
|
||||
v-if="!!errorMessage"
|
||||
class="mt-4"
|
||||
outlined
|
||||
type="error"
|
||||
>
|
||||
{{ errorMessage }}
|
||||
</v-alert>
|
||||
|
||||
<div class="card-actions">
|
||||
<button
|
||||
class="secondary"
|
||||
@click="$emit('closeRequested')"
|
||||
>
|
||||
{{ t('cancel') }}
|
||||
</button>
|
||||
<button
|
||||
:disabled="!canSave || isLoading"
|
||||
class="primary"
|
||||
@click="savePhoneNumber"
|
||||
>
|
||||
{{ t('save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useClient } from '@/plugins/api.js';
|
||||
import { useCreatorProfileStore } from '@/stores/creatorProfileStore.js';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useClient } from '@/plugins/api.js';
|
||||
import { useCreatorProfileStore } from '@/stores/creatorProfileStore.js';
|
||||
|
||||
const { t } = useI18n();
|
||||
const client = useClient();
|
||||
const creatorProfileStore = useCreatorProfileStore();
|
||||
const { t } = useI18n();
|
||||
const client = useClient();
|
||||
const creatorProfileStore = useCreatorProfileStore();
|
||||
|
||||
const props = defineProps({
|
||||
creator: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const props = defineProps({
|
||||
creator: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Format existing phone number to display format
|
||||
const formatPhoneForDisplay = (phone) => {
|
||||
if (!phone) return '';
|
||||
const digits = phone.replace(/\D/g, '');
|
||||
if (digits.length === 10) {
|
||||
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
||||
}
|
||||
return phone;
|
||||
};
|
||||
// Format existing phone number to display format
|
||||
const formatPhoneForDisplay = phone => {
|
||||
if (!phone) return '';
|
||||
const digits = phone.replace(/\D/g, '');
|
||||
if (digits.length === 10) {
|
||||
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
||||
}
|
||||
return phone;
|
||||
};
|
||||
|
||||
// Extract just the digits from formatted phone
|
||||
const extractDigits = (formattedPhone) => {
|
||||
return formattedPhone.replace(/\D/g, '');
|
||||
};
|
||||
// Extract just the digits from formatted phone
|
||||
const extractDigits = formattedPhone => {
|
||||
return formattedPhone.replace(/\D/g, '');
|
||||
};
|
||||
|
||||
const displayPhoneNumber = ref(formatPhoneForDisplay(props.creator.presentation?.phoneNumber || ''));
|
||||
const phoneDigits = ref(extractDigits(displayPhoneNumber.value));
|
||||
const isLoading = ref(false);
|
||||
const errorMessage = ref('');
|
||||
const displayPhoneNumber = ref(formatPhoneForDisplay(props.creator.presentation?.phoneNumber || ''));
|
||||
const phoneDigits = ref(extractDigits(displayPhoneNumber.value));
|
||||
const isLoading = ref(false);
|
||||
const errorMessage = ref('');
|
||||
|
||||
// Phone number formatting and validation
|
||||
const formatPhoneNumber = (digits) => {
|
||||
// Remove all non-digits
|
||||
const cleaned = digits.replace(/\D/g, '');
|
||||
|
||||
// Apply formatting based on length
|
||||
if (cleaned.length === 0) return '';
|
||||
if (cleaned.length <= 3) return `(${cleaned}`;
|
||||
if (cleaned.length <= 6) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`;
|
||||
return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6, 10)}`;
|
||||
};
|
||||
// Phone number formatting and validation
|
||||
const formatPhoneNumber = digits => {
|
||||
// Remove all non-digits
|
||||
const cleaned = digits.replace(/\D/g, '');
|
||||
|
||||
const handlePhoneInput = (event) => {
|
||||
const input = event.target.value;
|
||||
const digits = extractDigits(input);
|
||||
|
||||
// Limit to 10 digits
|
||||
if (digits.length > 10) return;
|
||||
|
||||
phoneDigits.value = digits;
|
||||
displayPhoneNumber.value = formatPhoneNumber(digits);
|
||||
};
|
||||
// Apply formatting based on length
|
||||
if (cleaned.length === 0) return '';
|
||||
if (cleaned.length <= 3) return `(${cleaned}`;
|
||||
if (cleaned.length <= 6) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`;
|
||||
return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6, 10)}`;
|
||||
};
|
||||
|
||||
const handleKeydown = (event) => {
|
||||
// Allow backspace, delete, tab, escape, enter
|
||||
if ([8, 9, 27, 13, 46].includes(event.keyCode)) return;
|
||||
|
||||
// Allow Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X
|
||||
if ((event.ctrlKey || event.metaKey) && [65, 67, 86, 88].includes(event.keyCode)) return;
|
||||
|
||||
// Allow arrow keys
|
||||
if (event.keyCode >= 35 && event.keyCode <= 40) return;
|
||||
|
||||
// Only allow numbers (0-9)
|
||||
if (event.keyCode < 48 || event.keyCode > 57) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
const handlePhoneInput = event => {
|
||||
const input = event.target.value;
|
||||
const digits = extractDigits(input);
|
||||
|
||||
// Watch for changes to phoneDigits to update display
|
||||
watch(phoneDigits, (newDigits) => {
|
||||
displayPhoneNumber.value = formatPhoneNumber(newDigits);
|
||||
});
|
||||
// Limit to 10 digits
|
||||
if (digits.length > 10) return;
|
||||
|
||||
const isValidPhoneNumber = (digits) => {
|
||||
return digits.length === 10;
|
||||
};
|
||||
phoneDigits.value = digits;
|
||||
displayPhoneNumber.value = formatPhoneNumber(digits);
|
||||
};
|
||||
|
||||
const phoneRules = [
|
||||
v => {
|
||||
const digits = extractDigits(v);
|
||||
return digits.length > 0 || t('validation.phoneRequired');
|
||||
},
|
||||
v => {
|
||||
const digits = extractDigits(v);
|
||||
return isValidPhoneNumber(digits) || t('validation.phoneInvalid');
|
||||
},
|
||||
];
|
||||
const handleKeydown = event => {
|
||||
// Allow backspace, delete, tab, escape, enter
|
||||
if ([8, 9, 27, 13, 46].includes(event.keyCode)) return;
|
||||
|
||||
const phoneErrors = computed(() => {
|
||||
if (phoneDigits.value.length === 0) {
|
||||
return [t('validation.phoneRequired')];
|
||||
}
|
||||
if (!isValidPhoneNumber(phoneDigits.value)) {
|
||||
return [t('validation.phoneInvalid')];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
// Allow Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X
|
||||
if ((event.ctrlKey || event.metaKey) && [65, 67, 86, 88].includes(event.keyCode)) return;
|
||||
|
||||
const canSave = computed(() => {
|
||||
return phoneDigits.value.length === 10 &&
|
||||
phoneDigits.value !== extractDigits(props.creator.presentation?.phoneNumber || '');
|
||||
});
|
||||
// Allow arrow keys
|
||||
if (event.keyCode >= 35 && event.keyCode <= 40) return;
|
||||
|
||||
async function savePhoneNumber() {
|
||||
if (!props.creator.id) {
|
||||
console.error("Creator ID is missing!");
|
||||
return;
|
||||
}
|
||||
// Only allow numbers (0-9)
|
||||
if (event.keyCode < 48 || event.keyCode > 57) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
if (!canSave.value) {
|
||||
return;
|
||||
}
|
||||
// Watch for changes to phoneDigits to update display
|
||||
watch(phoneDigits, newDigits => {
|
||||
displayPhoneNumber.value = formatPhoneNumber(newDigits);
|
||||
});
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = '';
|
||||
const isValidPhoneNumber = digits => {
|
||||
return digits.length === 10;
|
||||
};
|
||||
|
||||
// Save the formatted phone number
|
||||
const formattedPhone = formatPhoneNumber(phoneDigits.value);
|
||||
|
||||
await client.post(
|
||||
`/api/creators/${props.creator.id}/phone`,
|
||||
{
|
||||
phoneNumber: formattedPhone
|
||||
}
|
||||
);
|
||||
const phoneRules = [
|
||||
v => {
|
||||
const digits = extractDigits(v);
|
||||
return digits.length > 0 || t('validation.phoneRequired');
|
||||
},
|
||||
v => {
|
||||
const digits = extractDigits(v);
|
||||
return isValidPhoneNumber(digits) || t('validation.phoneInvalid');
|
||||
},
|
||||
];
|
||||
|
||||
// Refresh creator profile
|
||||
await creatorProfileStore.fetchCreatorProfile();
|
||||
|
||||
// Close dialog
|
||||
emit('closeRequested');
|
||||
} catch (error) {
|
||||
console.error("Error saving phone number:", error);
|
||||
if (error?.response?.data?.errors) {
|
||||
errorMessage.value = error.response.data.errors[0]?.['reason'] || t('errors.unexpected');
|
||||
} else {
|
||||
errorMessage.value = error?.response?.data?.message || error.message || t('errors.unexpected');
|
||||
const phoneErrors = computed(() => {
|
||||
if (phoneDigits.value.length === 0) {
|
||||
return [t('validation.phoneRequired')];
|
||||
}
|
||||
if (!isValidPhoneNumber(phoneDigits.value)) {
|
||||
return [t('validation.phoneInvalid')];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const canSave = computed(() => {
|
||||
return (
|
||||
phoneDigits.value.length === 10 &&
|
||||
phoneDigits.value !== extractDigits(props.creator.presentation?.phoneNumber || '')
|
||||
);
|
||||
});
|
||||
|
||||
async function savePhoneNumber() {
|
||||
if (!props.creator.id) {
|
||||
console.error('Creator ID is missing!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canSave.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = '';
|
||||
|
||||
// Save the formatted phone number
|
||||
const formattedPhone = formatPhoneNumber(phoneDigits.value);
|
||||
|
||||
await client.post(`/api/creators/${props.creator.id}/phone`, {
|
||||
phoneNumber: formattedPhone,
|
||||
});
|
||||
|
||||
// Refresh creator profile
|
||||
await creatorProfileStore.fetchCreatorProfile();
|
||||
|
||||
// Close dialog
|
||||
emit('closeRequested');
|
||||
} catch (error) {
|
||||
console.error('Error saving phone number:', error);
|
||||
if (error?.response?.data?.errors) {
|
||||
errorMessage.value = error.response.data.errors[0]?.['reason'] || t('errors.unexpected');
|
||||
} else {
|
||||
errorMessage.value = error?.response?.data?.message || error.message || t('errors.unexpected');
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['closeRequested']);
|
||||
const emit = defineEmits(['closeRequested']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog {
|
||||
@apply max-w-md mx-auto;
|
||||
}
|
||||
.dialog {
|
||||
@apply max-w-md mx-auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
{
|
||||
"en": {
|
||||
"changePhoneNumber": "Change Phone Number",
|
||||
"phoneNumber": "Phone Number",
|
||||
"phonePlaceholder": "(555) 123-4567",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"validation": {
|
||||
"phoneRequired": "Phone number is required",
|
||||
"phoneInvalid": "Please enter a complete 10-digit phone number"
|
||||
"en": {
|
||||
"changePhoneNumber": "Change Phone Number",
|
||||
"phoneNumber": "Phone Number",
|
||||
"phonePlaceholder": "(555) 123-4567",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"validation": {
|
||||
"phoneRequired": "Phone number is required",
|
||||
"phoneInvalid": "Please enter a complete 10-digit phone number"
|
||||
},
|
||||
"errors": {
|
||||
"unexpected": "An unexpected error occurred"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"unexpected": "An unexpected error occurred"
|
||||
"fr": {
|
||||
"changePhoneNumber": "Modifier le numéro de téléphone",
|
||||
"phoneNumber": "Numéro de téléphone",
|
||||
"phonePlaceholder": "(555) 123-4567",
|
||||
"save": "Enregistrer",
|
||||
"cancel": "Annuler",
|
||||
"validation": {
|
||||
"phoneRequired": "Le numéro de téléphone est requis",
|
||||
"phoneInvalid": "Veuillez entrer un numéro de téléphone complet à 10 chiffres"
|
||||
},
|
||||
"errors": {
|
||||
"unexpected": "Une erreur inattendue s'est produite"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fr": {
|
||||
"changePhoneNumber": "Modifier le numéro de téléphone",
|
||||
"phoneNumber": "Numéro de téléphone",
|
||||
"phonePlaceholder": "(555) 123-4567",
|
||||
"save": "Enregistrer",
|
||||
"cancel": "Annuler",
|
||||
"validation": {
|
||||
"phoneRequired": "Le numéro de téléphone est requis",
|
||||
"phoneInvalid": "Veuillez entrer un numéro de téléphone complet à 10 chiffres"
|
||||
},
|
||||
"errors": {
|
||||
"unexpected": "Une erreur inattendue s'est produite"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"changePhoneNumber": "Cambiar número de teléfono",
|
||||
"phoneNumber": "Número de teléfono",
|
||||
"phonePlaceholder": "(555) 123-4567",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"validation": {
|
||||
"phoneRequired": "El número de teléfono es obligatorio",
|
||||
"phoneInvalid": "Por favor ingrese un número de teléfono completo de 10 dígitos"
|
||||
},
|
||||
"errors": {
|
||||
"unexpected": "Se produjo un error inesperado"
|
||||
}
|
||||
}
|
||||
}
|
||||
</i18n>
|
||||
Reference in New Issue
Block a user