new base for gitea

This commit is contained in:
EvanChal
2025-12-23 19:12:30 +01:00
parent 849167994c
commit e916dd7cd0
147 changed files with 757 additions and 15674 deletions

341
frontend/src/views/Register.vue Executable file → Normal file
View File

@@ -1,96 +1,141 @@
<template>
<div>
<h2 class="text-2xl font-bold text-gray-900 mb-6">Inscription</h2>
<form @submit.prevent="handleRegister" class="space-y-4">
<div>
<label for="email" class="label">Email</label>
<input
id="email"
v-model="form.email"
type="email"
required
:disabled="!registrationEnabled"
class="input"
:class="{ 'opacity-50 cursor-not-allowed': !registrationEnabled }"
placeholder="ton.email@example.com"
>
<div class="space-y-6">
<!-- Header -->
<div class="text-center">
<h2 class="text-2xl font-bold text-gray-900">Créer un compte</h2>
<p class="mt-2 text-sm text-gray-500">Rejoignez notre communauté en quelques étapes</p>
</div>
<!-- Progress Bar -->
<div class="pt-4">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-700">Étape {{ currentStep }} sur {{ totalSteps }}</span>
<span class="text-sm font-medium text-gray-500">{{ Math.round((currentStep / totalSteps) * 100) }}%</span>
</div>
<div>
<label for="username" class="label">Nom d'utilisateur</label>
<input
id="username"
v-model="form.username"
type="text"
required
minlength="3"
maxlength="50"
:disabled="!registrationEnabled"
class="input"
:class="{ 'opacity-50 cursor-not-allowed': !registrationEnabled }"
placeholder="tonpseudo"
>
<div class="w-full bg-gray-200 rounded-full h-1.5">
<div
class="bg-primary-600 h-1.5 rounded-full transition-all duration-500 ease-in-out"
:style="{ width: `${(currentStep / totalSteps) * 100}%` }"
></div>
</div>
<div>
<label for="full_name" class="label">Nom complet</label>
<input
id="full_name"
v-model="form.full_name"
type="text"
required
:disabled="!registrationEnabled"
class="input"
:class="{ 'opacity-50 cursor-not-allowed': !registrationEnabled }"
placeholder="Prénom Nom"
>
</div>
<div>
<label for="password" class="label">Mot de passe</label>
<input
id="password"
v-model="form.password"
type="password"
required
minlength="6"
:disabled="!registrationEnabled"
class="input"
:class="{ 'opacity-50 cursor-not-allowed': !registrationEnabled }"
placeholder="••••••••"
>
</div>
<div>
<label for="password_confirm" class="label">Confirmer le mot de passe</label>
<input
id="password_confirm"
v-model="form.password_confirm"
type="password"
required
minlength="6"
:disabled="!registrationEnabled"
class="input"
:class="{ 'opacity-50 cursor-not-allowed': !registrationEnabled }"
placeholder="••••••••"
>
</div>
<div v-if="error" class="text-red-600 text-sm">
{{ error }}
</div>
</div>
<!-- Step Content -->
<div class="min-h-[350px] flex flex-col justify-center">
<StepTransition :step="currentStep">
<!-- Step 1: Welcome -->
<div v-if="currentStep === 1" class="text-center">
<h3 class="text-2xl font-bold text-gray-900 mb-4">Bienvenue sur LeDiscord !</h3>
<p class="text-gray-600 max-w-sm mx-auto">
Nous sommes ravis de vous accueillir. Préparez-vous à rejoindre une communauté passionnante.
</p>
</div>
<!-- Step 2: Registration Form -->
<div v-if="currentStep === 2">
<form @submit.prevent="nextStep" class="space-y-6">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
<div>
<label for="email" class="label">Email</label>
<input id="email" v-model="form.email" type="email" required class="input" @blur="touchedFields.email = true">
</div>
<div>
<label for="username" class="label">Nom d'utilisateur</label>
<input id="username" v-model="form.username" type="text" required minlength="3" class="input" @blur="touchedFields.username = true">
</div>
</div>
<div>
<label for="full_name" class="label">Nom complet</label>
<input id="full_name" v-model="form.full_name" type="text" required class="input" @blur="touchedFields.full_name = true">
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4">
<div>
<label for="password" class="label">Mot de passe</label>
<input id="password" v-model="form.password" type="password" required minlength="6" class="input" @blur="touchedFields.password = true">
</div>
<div>
<label for="password_confirm" class="label">Confirmer</label>
<input id="password_confirm" v-model="form.password_confirm" type="password" required class="input" @blur="touchedFields.password_confirm = true">
</div>
</div>
<PasswordStrength :password="form.password" />
<div v-if="touchedFields.password_confirm && form.password_confirm && form.password !== form.password_confirm" class="flex items-center text-sm text-red-600">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /></svg>
Les mots de passe ne correspondent pas
</div>
</form>
</div>
<!-- Step 3: Warning -->
<div v-if="currentStep === 3" class="text-center">
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3 text-left">
<p class="text-sm text-yellow-700">
LeDiscord est actuellement en version bêta. Votre retour est précieux pour nous aider à améliorer la plateforme.
</p>
</div>
</div>
</div>
</div>
<!-- Step 4: Features Tour -->
<div v-if="currentStep === 4" class="text-center">
<h3 class="text-2xl font-bold text-gray-900 mb-4">Découvrez les fonctionnalités</h3>
<div class="space-y-4">
<div v-for="feature in features" :key="feature.title" class="border rounded-lg p-4 text-left flex items-center space-x-4">
<div class="w-10 h-10 bg-gray-100 rounded-lg flex items-center justify-center">
<span v-html="feature.icon"></span>
</div>
<div>
<h4 class="font-semibold">{{ feature.title }}</h4>
<p class="text-sm text-gray-600">{{ feature.description }}</p>
</div>
</div>
</div>
</div>
</StepTransition>
</div>
<!-- Navigation Buttons -->
<div class="flex items-center pt-6" :class="currentStep > 1 ? 'justify-between' : 'justify-end'">
<button
type="submit"
:disabled="loading || !registrationEnabled"
class="w-full btn-primary"
v-if="currentStep > 1"
@click="previousStep"
class="btn-secondary"
:disabled="loading"
>Précédent</button>
<button
v-if="currentStep < totalSteps"
@click="nextStep"
:disabled="!canProceed || loading"
class="btn-primary bg-gradient-to-r from-primary-500 to-purple-600"
:class="{ 'opacity-50 cursor-not-allowed': !canProceed }"
>Suivant</button>
<button
v-if="currentStep === totalSteps"
@click="handleRegister"
:disabled="loading"
class="btn-primary bg-gradient-to-r from-primary-500 to-purple-600"
>
{{ loading ? 'Inscription...' : !registrationEnabled ? 'Inscriptions désactivées' : 'S\'inscrire' }}
<span v-if="loading">Création en cours...</span>
<span v-else>Créer mon compte</span>
</button>
</form>
</div>
<div class="mt-6 text-center">
<div v-if="error" class="mt-4 text-center text-red-600">
{{ error }}
</div>
<!-- Login Link -->
<div class="mt-8 text-center">
<p class="text-sm text-gray-600">
Déjà un compte ?
<router-link to="/login" class="font-medium text-primary-600 hover:text-primary-500">
@@ -98,33 +143,25 @@
</router-link>
</p>
</div>
<!-- Vérification du statut d'inscription -->
<div v-if="!registrationEnabled" class="mt-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800">Inscriptions désactivées</h3>
<p class="text-sm text-yellow-700 mt-1">
Les nouvelles inscriptions sont temporairement désactivées. Veuillez contacter l'administrateur.
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import axios from '@/utils/axios'
import StepTransition from '@/components/StepTransition.vue'
import PasswordStrength from '@/components/PasswordStrength.vue'
import FormValidation from '@/components/FormValidation.vue'
const authStore = useAuthStore()
const router = useRouter()
// Step management
const currentStep = ref(1)
const totalSteps = 4
// Form data
const form = ref({
email: '',
username: '',
@@ -133,23 +170,63 @@ const form = ref({
password_confirm: ''
})
const touchedFields = ref({
email: false,
username: false,
full_name: false,
password: false,
password_confirm: false
})
const loading = ref(false)
const error = ref('')
const registrationEnabled = ref(true)
// Features for the tour
const features = ref([
{
title: 'Posts & Discussions',
description: 'Partagez vos idées et participez.',
icon: '<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path></svg>'
},
{
title: 'Vlogs & Vidéos',
description: 'Regardez des vidéos de la communauté.',
icon: '<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>'
},
{
title: 'Événements',
description: 'Participez à des rencontres.',
icon: '<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>'
}
])
// Computed properties
const canProceed = computed(() => {
if (currentStep.value === 2) {
return form.value.email && form.value.username && form.value.full_name &&
form.value.password && form.value.password.length >= 6 &&
form.value.password === form.value.password_confirm
}
return true
})
// Methods
function nextStep() {
if (currentStep.value < totalSteps && canProceed.value) {
currentStep.value++
}
}
function previousStep() {
if (currentStep.value > 1) {
currentStep.value--
}
}
async function handleRegister() {
error.value = ''
if (!registrationEnabled.value) {
error.value = 'Les inscriptions sont actuellement désactivées'
return
}
if (form.value.password !== form.value.password_confirm) {
error.value = 'Les mots de passe ne correspondent pas'
return
}
if (!canProceed.value) return
loading.value = true
const result = await authStore.register({
email: form.value.email,
@@ -158,33 +235,11 @@ async function handleRegister() {
password: form.value.password
})
if (!result.success) {
error.value = result.error
if (result.success) {
router.push('/')
} else {
error.value = result.error || 'Une erreur est survenue lors de l\'inscription.'
}
loading.value = false
}
async function checkRegistrationStatus() {
try {
const response = await axios.get('/api/settings/public/registration-status')
const status = response.data
registrationEnabled.value = status.can_register
// Afficher des informations supplémentaires si l'inscription est désactivée
if (!status.registration_enabled) {
console.log('Registration disabled by admin')
} else if (status.current_users_count >= status.max_users) {
console.log(`Maximum users reached: ${status.current_users_count}/${status.max_users}`)
}
} catch (error) {
console.error('Error checking registration status:', error)
// En cas d'erreur, on désactive l'inscription pour éviter les problèmes de sécurité
registrationEnabled.value = false
}
}
onMounted(() => {
checkRegistrationStatus()
})
</script>