Improve the name-editor

This commit is contained in:
2025-04-19 03:49:27 -04:00
parent 98c598f3c6
commit 300ee124d8
4 changed files with 68 additions and 12 deletions

View File

@@ -1,4 +1,6 @@
VITE_API_URL=https://localhost:5001/
VITE_APP_BASE_URL=http://localhost:5173
VITE_APP_API_URL=http://localhost:5173/api
VITE_STRIPE_API_KEY=pk_test_51OoveVDrRyqXtNdB2st1NgA8WQA9rhgGaf3q7bCpAOoQyyRS30HMCzGeHba7meVGCSPfb1BVWmOTmFOcr9MkKf5H00bLu5MqsS
VITE_GOOGLE_CLIENT_ID=213344094492-9dbaet2gaschju3hj1sgv1umk0qpd833.apps.googleusercontent.com
VITE_FACEBOOK_APP_ID

View File

@@ -1,4 +1,6 @@
VITE_API_URL=https://hutopy-backend-api.azurewebsites.net
VITE_APP_BASE_URL=https://hutopy.ca
VITE_APP_API_URL=https://hutopy.ca/api
VITE_STRIPE_API_KEY=51OoveVDrRyqXtNdBAxIo183PujtqFyU0xUMK9YNtIijcHeDlcLN6pqkZWHbgaBA0FHrwLMSoy3yVLN33NX8ExOxL00MSZwgJN7
VITE_GOOGLE_CLIENT_ID=213344094492-7c83lqoh7mnjgadpeqo2lcs1krhbsnnd.apps.googleusercontent.com
AZURE_SUBSCRIPTION_ID=46feb20f-3ae1-495a-830b-a31f7b76483d

7
frontend/src/config.js Normal file
View File

@@ -0,0 +1,7 @@
// Environment-specific configuration
const config = {
baseUrl: import.meta.env.VITE_APP_BASE_URL || 'https://hutopy.ca',
apiUrl: import.meta.env.VITE_APP_API_URL || 'https://hutopy.ca/api',
};
export default config;

View File

@@ -2,7 +2,8 @@
import {ref, onMounted, onUnmounted, computed} from "vue";
import {v7} from "uuid";
import {useClient} from "@/plugins/api.js";
import { useI18n } from 'vue-i18n';
import {useI18n} from 'vue-i18n';
import config from '@/config';
const props = defineProps({
name: {
@@ -23,10 +24,11 @@ const emits = defineEmits([
]);
const name = ref(props.name);
const { t } = useI18n();
const {t} = useI18n();
const isOperationPending = ref(false);
const reservationState = ref(null);
const validationError = ref('');
// Use the reservationId from props if provided, otherwise generate a new one
const reservationId = ref(props.creatorNameReservationId || v7());
@@ -36,6 +38,27 @@ const isCurrentSlug = computed(() => {
return props.originalSlug && name.value === props.originalSlug;
});
// Base URL for display
const baseUrl = computed(() => `${config.baseUrl}/@`);
// Validation function for the slug
const validateSlug = (slug) => {
if (!slug) {
validationError.value = t('creator.name.errors.required');
return false;
}
// Only allow letters, numbers, and hyphens
const validSlugRegex = /^[a-zA-Z0-9-]+$/;
if (!validSlugRegex.test(slug)) {
validationError.value = t('creator.name.errors.invalid');
return false;
}
validationError.value = '';
return true;
};
// Ensure we emit the reservationId on mount if we generated a new one
onMounted(() => {
if (!props.creatorNameReservationId) {
@@ -68,6 +91,12 @@ const handleInput = () => {
return; // Skip if we've already processed this exact name
}
// Validate the slug
if (!validateSlug(currentName)) {
reservationState.value = "unavailable";
return;
}
// If the name is the same as the original slug, set reservation state to "reserved"
if (props.originalSlug && currentName === props.originalSlug) {
reservationState.value = "reserved";
@@ -100,9 +129,9 @@ const checkNameAvailability = async (nameToCheck) => {
currentController = controller;
await client.post(
`/api/creators/@${encodeURIComponent(nameToCheck)}/reserve`,
{ reservationId: reservationId.value },
{ signal: controller.signal }
`/api/creators/@${encodeURIComponent(nameToCheck)}/reserve`,
{reservationId: reservationId.value},
{signal: controller.signal}
);
// Only process the response if this is still the current request
@@ -137,9 +166,13 @@ onUnmounted(() => {
variant="outlined"
:label="t('creator.name.label')"
v-model="name"
outlined
@input="handleInput"
:error-messages="validationError"
>
<template #prepend-inner>
<span class="text-gray-400 font-sans">{{ baseUrl }}</span>
</template>
<template #append-inner>
<v-progress-circular
v-if="reservationState === 'loading'"
@@ -164,21 +197,33 @@ onUnmounted(() => {
"en": {
"creator": {
"name": {
"label": "Your creator handle"
"label": "Your creator handle",
"errors": {
"required": "Creator handle is required",
"invalid": "Only letters, numbers, and hyphens are allowed"
}
}
}
},
"fr": {
"creator": {
"name": {
"label": "Votre identifiant de créateur"
"label": "Votre identifiant de créateur",
"errors": {
"required": "L'identifiant est requis",
"invalid": "Seules les lettres, chiffres et tirets sont autorisés"
}
}
}
},
"es": {
"creator": {
"name": {
"label": "Tu identificador de creador"
"label": "Tu identificador de creador",
"errors": {
"required": "El identificador es obligatorio",
"invalid": "Solo se permiten letras, números y guiones"
}
}
}
}