add docker + debug
This commit is contained in:
parent
848a79a04e
commit
1f4193f9c0
26 changed files with 21051 additions and 28 deletions
20
frontend/.eslintrc.js
Normal file
20
frontend/.eslintrc.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: '@babel/eslint-parser',
|
||||
requireConfigFile: false,
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
||||
18
frontend/Dockerfile
Normal file
18
frontend/Dockerfile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
FROM node:16-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package.json and package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application
|
||||
COPY . .
|
||||
|
||||
# Expose port 8012
|
||||
EXPOSE 8012
|
||||
|
||||
# Start the application
|
||||
CMD ["npm", "run", "serve"]
|
||||
5
frontend/babel.config.js
Normal file
5
frontend/babel.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
181
frontend/docs/USER_GUIDE.md
Normal file
181
frontend/docs/USER_GUIDE.md
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
# Guide d'utilisation de Managerr
|
||||
|
||||
## Table des matières
|
||||
1. [Introduction](#introduction)
|
||||
2. [Premiers pas](#premiers-pas)
|
||||
- [Inscription](#inscription)
|
||||
- [Connexion](#connexion)
|
||||
3. [Interface principale](#interface-principale)
|
||||
- [Barre de navigation](#barre-de-navigation)
|
||||
- [Thème clair/sombre](#thème-clairsombre)
|
||||
4. [Tableau de bord](#tableau-de-bord)
|
||||
- [Agenda mensuel](#agenda-mensuel)
|
||||
- [Prochains téléchargements](#prochains-téléchargements)
|
||||
5. [Gestion des films](#gestion-des-films)
|
||||
- [Liste des films](#liste-des-films)
|
||||
- [Films manquants](#films-manquants)
|
||||
- [Films à venir](#films-à-venir)
|
||||
- [Historique](#historique-des-films)
|
||||
6. [Gestion des séries](#gestion-des-séries)
|
||||
- [Liste des séries](#liste-des-séries)
|
||||
- [Épisodes manquants](#épisodes-manquants)
|
||||
- [Épisodes à venir](#épisodes-à-venir)
|
||||
- [Historique](#historique-des-séries)
|
||||
7. [Paramètres](#paramètres)
|
||||
- [Configuration de Sonarr](#configuration-de-sonarr)
|
||||
- [Configuration de Radarr](#configuration-de-radarr)
|
||||
- [Test des connexions](#test-des-connexions)
|
||||
8. [Conseils et astuces](#conseils-et-astuces)
|
||||
9. [Dépannage](#dépannage)
|
||||
|
||||
## Introduction
|
||||
|
||||
Managerr est une application web qui vous permet de gérer vos films et séries TV à partir d'une interface unifiée en utilisant les API de Sonarr et Radarr. Ce guide vous aidera à prendre en main l'application et à utiliser toutes ses fonctionnalités.
|
||||
|
||||
## Premiers pas
|
||||
|
||||
### Inscription
|
||||
|
||||
1. Accédez à la page d'accueil de l'application
|
||||
2. Cliquez sur "S'inscrire"
|
||||
3. Remplissez le formulaire avec votre nom d'utilisateur, votre email et un mot de passe sécurisé
|
||||
4. Cliquez sur "Créer un compte"
|
||||
|
||||
### Connexion
|
||||
|
||||
1. Accédez à la page d'accueil de l'application
|
||||
2. Cliquez sur "Se connecter"
|
||||
3. Entrez votre email et votre mot de passe
|
||||
4. Cliquez sur "Connexion"
|
||||
|
||||
## Interface principale
|
||||
|
||||
### Barre de navigation
|
||||
|
||||
La barre de navigation vous permet d'accéder aux différentes sections de l'application :
|
||||
- **Tableau de bord** : Vue d'ensemble avec l'agenda mensuel
|
||||
- **Films** : Gestion de votre collection de films
|
||||
- **Séries** : Gestion de vos séries TV
|
||||
- **Paramètres** : Configuration des API Sonarr et Radarr
|
||||
- **Déconnexion** : Pour quitter votre session
|
||||
|
||||
### Thème clair/sombre
|
||||
|
||||
Managerr prend en charge les thèmes clair et sombre :
|
||||
- Le bouton de bascule du thème se trouve dans la barre de navigation
|
||||
- Par défaut, l'application utilise les préférences de votre système
|
||||
- Vous pouvez basculer manuellement entre les modes clair et sombre à tout moment
|
||||
|
||||
## Tableau de bord
|
||||
|
||||
### Agenda mensuel
|
||||
|
||||
L'agenda mensuel affiche tous les films et épisodes à venir :
|
||||
- Naviguez entre les mois avec les flèches de navigation
|
||||
- Chaque jour contenant des sorties est marqué d'un indicateur
|
||||
- Cliquez sur un jour pour voir les détails des sorties prévues
|
||||
|
||||
### Prochains téléchargements
|
||||
|
||||
Cette section affiche la liste des prochains films et épisodes qui seront téléchargés, triés par date.
|
||||
|
||||
## Gestion des films
|
||||
|
||||
### Liste des films
|
||||
|
||||
- Affiche tous les films de votre bibliothèque Radarr
|
||||
- Recherche et filtre par titre, genre, année, etc.
|
||||
- Tri par différents critères (titre, date d'ajout, etc.)
|
||||
- Cliquez sur un film pour voir ses détails
|
||||
|
||||
### Films manquants
|
||||
|
||||
- Affiche les films qui sont dans votre bibliothèque mais pas encore téléchargés
|
||||
- Indique le statut de recherche pour chaque film
|
||||
- Permet de déclencher une recherche manuelle
|
||||
|
||||
### Films à venir
|
||||
|
||||
- Affiche les films dont la sortie est prévue prochainement
|
||||
- Indique les dates de sortie au cinéma et en Blu-ray/DVD
|
||||
|
||||
### Historique des films
|
||||
|
||||
- Affiche l'historique des téléchargements de films
|
||||
- Inclut les informations sur la qualité et la date de téléchargement
|
||||
|
||||
## Gestion des séries
|
||||
|
||||
### Liste des séries
|
||||
|
||||
- Affiche toutes les séries de votre bibliothèque Sonarr
|
||||
- Recherche et filtre par titre, genre, réseau, etc.
|
||||
- Tri par différents critères (titre, date d'ajout, etc.)
|
||||
- Cliquez sur une série pour voir ses détails et la liste des saisons/épisodes
|
||||
|
||||
### Épisodes manquants
|
||||
|
||||
- Affiche les épisodes qui sont dans votre bibliothèque mais pas encore téléchargés
|
||||
- Regroupés par série et saison
|
||||
- Permet de déclencher une recherche manuelle
|
||||
|
||||
### Épisodes à venir
|
||||
|
||||
- Affiche les épisodes dont la diffusion est prévue prochainement
|
||||
- Indique les dates de diffusion et les chaînes/réseaux
|
||||
|
||||
### Historique des séries
|
||||
|
||||
- Affiche l'historique des téléchargements d'épisodes
|
||||
- Inclut les informations sur la qualité et la date de téléchargement
|
||||
|
||||
## Paramètres
|
||||
|
||||
### Configuration de Sonarr
|
||||
|
||||
1. Accédez à la page "Paramètres"
|
||||
2. Dans la section Sonarr, entrez :
|
||||
- L'URL de votre serveur Sonarr (ex : http://192.168.1.100:8989)
|
||||
- Votre clé API Sonarr (disponible dans les paramètres de Sonarr)
|
||||
3. Cliquez sur "Tester la connexion" pour vérifier que tout fonctionne
|
||||
4. Cliquez sur "Enregistrer" pour sauvegarder vos paramètres
|
||||
|
||||
### Configuration de Radarr
|
||||
|
||||
1. Accédez à la page "Paramètres"
|
||||
2. Dans la section Radarr, entrez :
|
||||
- L'URL de votre serveur Radarr (ex : http://192.168.1.100:7878)
|
||||
- Votre clé API Radarr (disponible dans les paramètres de Radarr)
|
||||
3. Cliquez sur "Tester la connexion" pour vérifier que tout fonctionne
|
||||
4. Cliquez sur "Enregistrer" pour sauvegarder vos paramètres
|
||||
|
||||
### Test des connexions
|
||||
|
||||
- Le bouton "Tester la connexion" vérifie que :
|
||||
- L'URL est accessible
|
||||
- La clé API est valide
|
||||
- La version de l'API est compatible
|
||||
- Un message vous informera du résultat du test
|
||||
|
||||
## Conseils et astuces
|
||||
|
||||
- **Mise en cache** : L'application met en cache certaines données pour améliorer les performances. Si vous ne voyez pas les changements récents, essayez de rafraîchir la page.
|
||||
- **Mode responsive** : L'application s'adapte automatiquement à tous les appareils, y compris les smartphones et les tablettes.
|
||||
- **Raccourcis clavier** : Utilisez la touche "T" pour basculer rapidement entre les thèmes clair et sombre.
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Problèmes de connexion aux API
|
||||
|
||||
1. Vérifiez que vos serveurs Sonarr et Radarr sont en cours d'exécution
|
||||
2. Assurez-vous que les URLs sont correctes et incluent le port
|
||||
3. Vérifiez que vos clés API sont valides
|
||||
4. Assurez-vous que votre réseau permet les connexions aux ports de Sonarr et Radarr
|
||||
|
||||
### Problèmes d'affichage
|
||||
|
||||
1. Essayez d'actualiser la page
|
||||
2. Videz le cache de votre navigateur
|
||||
3. Essayez un autre navigateur pour voir si le problème persiste
|
||||
|
||||
Si vous rencontrez d'autres problèmes, n'hésitez pas à ouvrir une issue sur GitHub ou à contacter l'équipe de support.
|
||||
14268
frontend/package-lock.json
generated
Normal file
14268
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -10,19 +10,23 @@
|
|||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"core-js": "^3.33.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"vue": "^3.3.0",
|
||||
"vue-router": "^4.2.0",
|
||||
"vuex": "^4.1.0",
|
||||
"date-fns": "^2.30.0"
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/eslint-parser": "^7.28.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"image-webpack-loader": "^8.1.0",
|
||||
"sass": "^1.69.0",
|
||||
"sass-loader": "^13.3.0"
|
||||
}
|
||||
|
|
|
|||
133
frontend/src/services/apiService.js
Normal file
133
frontend/src/services/apiService.js
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// services/apiService.js
|
||||
// Service pour gérer les appels API avec mise en cache
|
||||
|
||||
import axios from 'axios'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
import cacheService from './cacheService'
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: process.env.VUE_APP_API_URL || 'http://localhost:5000/api',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// Intercepteur pour ajouter le token d'authentification
|
||||
apiClient.interceptors.request.use(
|
||||
config => {
|
||||
const token = store.getters.token
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => Promise.reject(error)
|
||||
)
|
||||
|
||||
// Intercepteur pour gérer les erreurs
|
||||
apiClient.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
// Déconnexion automatique si le token n'est plus valide
|
||||
store.dispatch('logout')
|
||||
router.push('/login')
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default {
|
||||
// Méthodes génériques avec cache
|
||||
async get(url, params = {}, useCache = true, cacheTTL = 5 * 60 * 1000) {
|
||||
const cacheKey = `get:${url}:${JSON.stringify(params)}`
|
||||
|
||||
// Vérifier le cache si demandé
|
||||
if (useCache && cacheService.has(cacheKey)) {
|
||||
return Promise.resolve(cacheService.get(cacheKey))
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiClient.get(url, { params })
|
||||
|
||||
// Mettre en cache la réponse si demandé
|
||||
if (useCache) {
|
||||
cacheService.set(cacheKey, response.data, cacheTTL)
|
||||
}
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('API GET error:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async post(url, data = {}, useCache = false, cacheTTL = 5 * 60 * 1000) {
|
||||
// Pour les POST, on n'utilise le cache que dans des cas spéciaux
|
||||
// indiqués par le paramètre useCache
|
||||
const cacheKey = `post:${url}:${JSON.stringify(data)}`
|
||||
|
||||
if (useCache && cacheService.has(cacheKey)) {
|
||||
return Promise.resolve(cacheService.get(cacheKey))
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiClient.post(url, data)
|
||||
|
||||
if (useCache) {
|
||||
cacheService.set(cacheKey, response.data, cacheTTL)
|
||||
}
|
||||
|
||||
// Invalider le cache GET pour ce endpoint
|
||||
this.invalidateCache(url)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('API POST error:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async put(url, data = {}) {
|
||||
try {
|
||||
const response = await apiClient.put(url, data)
|
||||
|
||||
// Invalider le cache pour ce endpoint
|
||||
this.invalidateCache(url)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('API PUT error:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
async delete(url) {
|
||||
try {
|
||||
const response = await apiClient.delete(url)
|
||||
|
||||
// Invalider le cache pour ce endpoint
|
||||
this.invalidateCache(url)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('API DELETE error:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// Méthode pour invalider le cache lié à une URL
|
||||
invalidateCache(url) {
|
||||
Object.keys(cacheService.cache).forEach(key => {
|
||||
if (key.includes(url)) {
|
||||
cacheService.invalidate(key)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// Méthode pour vider tout le cache
|
||||
clearCache() {
|
||||
cacheService.clear()
|
||||
}
|
||||
}
|
||||
69
frontend/src/services/cacheService.js
Normal file
69
frontend/src/services/cacheService.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// services/cacheService.js
|
||||
// Service pour gérer la mise en cache des données
|
||||
|
||||
class CacheService {
|
||||
constructor() {
|
||||
this.cache = {};
|
||||
this.defaultTTL = 5 * 60 * 1000; // 5 minutes en millisecondes
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une entrée en cache
|
||||
* @param {string} key - La clé de l'entrée
|
||||
* @returns {Object|null} - L'entrée ou null si non trouvée ou expirée
|
||||
*/
|
||||
get(key) {
|
||||
const item = this.cache[key];
|
||||
|
||||
if (!item) return null;
|
||||
|
||||
// Vérifier si l'entrée est expirée
|
||||
if (Date.now() > item.expiry) {
|
||||
// Suppression automatique des entrées expirées
|
||||
delete this.cache[key];
|
||||
return null;
|
||||
}
|
||||
|
||||
return item.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une entrée dans le cache
|
||||
* @param {string} key - La clé de l'entrée
|
||||
* @param {any} value - La valeur à stocker
|
||||
* @param {number} ttl - Durée de vie en millisecondes (par défaut: 5 minutes)
|
||||
*/
|
||||
set(key, value, ttl = this.defaultTTL) {
|
||||
this.cache[key] = {
|
||||
value,
|
||||
expiry: Date.now() + ttl
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une entrée existe et est valide
|
||||
* @param {string} key - La clé de l'entrée
|
||||
* @returns {boolean} - Vrai si l'entrée existe et n'est pas expirée
|
||||
*/
|
||||
has(key) {
|
||||
return this.get(key) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une entrée du cache
|
||||
* @param {string} key - La clé de l'entrée
|
||||
*/
|
||||
invalidate(key) {
|
||||
delete this.cache[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vide tout le cache
|
||||
*/
|
||||
clear() {
|
||||
this.cache = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Export d'une instance singleton
|
||||
export default new CacheService();
|
||||
|
|
@ -113,6 +113,17 @@ export default createStore({
|
|||
// Effacer les erreurs
|
||||
clearError({ commit }) {
|
||||
commit('CLEAR_ERROR')
|
||||
},
|
||||
|
||||
// Vérifier s'il existe des utilisateurs dans la base de données
|
||||
async checkUsersExist() {
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/auth/check-users-exist`)
|
||||
return response.data.usersExist
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la vérification des utilisateurs', error)
|
||||
return true // Par défaut, on suppose qu'il existe des utilisateurs pour éviter des comportements inattendus
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
61
frontend/src/views/Home.vue
Normal file
61
frontend/src/views/Home.vue
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div class="home">
|
||||
<h1>Bienvenue sur Managerr</h1>
|
||||
<p>La plateforme de gestion pour Sonarr et Radarr</p>
|
||||
<div class="actions">
|
||||
<router-link to="/login" class="btn btn-primary">Se connecter</router-link>
|
||||
<router-link to="/register" class="btn btn-secondary">Créer un compte</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Home',
|
||||
async created() {
|
||||
try {
|
||||
const usersExist = await this.$store.dispatch('checkUsersExist')
|
||||
if (!usersExist) {
|
||||
// S'il n'y a pas d'utilisateurs, rediriger directement vers la page d'inscription
|
||||
this.$router.push('/register')
|
||||
} else if (this.$store.getters.isAuthenticated) {
|
||||
// Si l'utilisateur est déjà authentifié, rediriger vers le tableau de bord
|
||||
this.$router.push('/dashboard')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la vérification des utilisateurs:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #2196F3;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
...mapGetters(['error', 'loading'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['login', 'clearError']),
|
||||
...mapActions(['login', 'clearError', 'checkUsersExist']),
|
||||
async handleLogin() {
|
||||
try {
|
||||
await this.login({
|
||||
|
|
@ -64,10 +64,17 @@ export default {
|
|||
} catch (error) {
|
||||
// L'erreur est gérée dans le store
|
||||
}
|
||||
},
|
||||
async checkFirstConnection() {
|
||||
const usersExist = await this.checkUsersExist()
|
||||
if (!usersExist) {
|
||||
this.$router.push('/register')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.clearError()
|
||||
this.checkFirstConnection()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
48
frontend/src/views/NotFound.vue
Normal file
48
frontend/src/views/NotFound.vue
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="not-found">
|
||||
<h1>404</h1>
|
||||
<h2>Page non trouvée</h2>
|
||||
<p>La page que vous cherchez n'existe pas ou a été déplacée.</p>
|
||||
<router-link to="/" class="home-link">Retour à l'accueil</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NotFound'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.not-found {
|
||||
text-align: center;
|
||||
padding: 4rem 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 6rem;
|
||||
margin: 0;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.home-link {
|
||||
display: inline-block;
|
||||
margin-top: 2rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
148
frontend/src/views/Register.vue
Normal file
148
frontend/src/views/Register.vue
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<template>
|
||||
<div class="register">
|
||||
<h2>{{ isFirstUser ? 'Création du compte administrateur' : 'Inscription' }}</h2>
|
||||
|
||||
<div v-if="isFirstUser" class="first-user-message">
|
||||
<p>Bienvenue sur Managerr ! Aucun compte n'existe encore dans le système.</p>
|
||||
<p>Veuillez créer un premier compte qui aura les droits d'administrateur.</p>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="register">
|
||||
<div class="form-group">
|
||||
<label for="username">Nom d'utilisateur</label>
|
||||
<input type="text" id="username" v-model="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" v-model="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Mot de passe</label>
|
||||
<input type="password" id="password" v-model="password" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword">Confirmer le mot de passe</label>
|
||||
<input type="password" id="confirmPassword" v-model="confirmPassword" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn-register">
|
||||
{{ isFirstUser ? 'Créer le compte administrateur' : 'S\'inscrire' }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="!isFirstUser" class="login-link">Déjà un compte ? <router-link to="/login">Se connecter</router-link></p>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Register',
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
isFirstUser: false
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
// Vérifier s'il s'agit du premier utilisateur
|
||||
try {
|
||||
const usersExist = await this.$store.dispatch('checkUsersExist')
|
||||
this.isFirstUser = !usersExist
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la vérification des utilisateurs:', error)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
register() {
|
||||
// Validation simple
|
||||
if (this.password !== this.confirmPassword) {
|
||||
alert("Les mots de passe ne correspondent pas");
|
||||
return;
|
||||
}
|
||||
|
||||
// Appel API pour s'inscrire
|
||||
this.$store.dispatch('register', {
|
||||
username: this.username,
|
||||
email: this.email,
|
||||
password: this.password
|
||||
}).then(() => {
|
||||
this.$router.push('/dashboard');
|
||||
}).catch(error => {
|
||||
console.error('Erreur d\'inscription:', error);
|
||||
alert("Échec de l'inscription. Veuillez réessayer.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.register {
|
||||
max-width: 400px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.btn-register {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-link {
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.login-link a {
|
||||
color: #2196F3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.first-user-message {
|
||||
background-color: #e3f2fd;
|
||||
border-left: 4px solid #2196F3;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.first-user-message p {
|
||||
margin: 0.5rem 0;
|
||||
color: #0d47a1;
|
||||
}
|
||||
</style>
|
||||
38
frontend/vue.config.js
Normal file
38
frontend/vue.config.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const { defineConfig } = require('@vue/cli-service')
|
||||
const path = require('path')
|
||||
|
||||
// Configuration simplifiée pour éviter les erreurs potentielles
|
||||
module.exports = defineConfig({
|
||||
devServer: {
|
||||
port: 8012, // Nouveau port pour le serveur de développement
|
||||
host: '0.0.0.0', // Permet d'accéder à l'application depuis l'extérieur du conteneur
|
||||
allowedHosts: 'all' // Autorise tous les hôtes à accéder à l'application
|
||||
},
|
||||
transpileDependencies: true,
|
||||
productionSourceMap: false, // Désactive les source maps en production pour réduire la taille
|
||||
lintOnSave: false, // Désactive temporairement la vérification ESLint au démarrage
|
||||
configureWebpack: {
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all', // Divise les chunks pour améliorer le chargement
|
||||
minSize: 20000,
|
||||
maxSize: 250000,
|
||||
}
|
||||
},
|
||||
performance: {
|
||||
hints: 'warning', // Affiche des avertissements pour les assets trop grands
|
||||
maxAssetSize: 512000, // Taille maximale d'un asset (500 KiB)
|
||||
maxEntrypointSize: 512000 // Taille maximale d'un point d'entrée (500 KiB)
|
||||
}
|
||||
},
|
||||
chainWebpack: config => {
|
||||
// Préchargement des routes pour améliorer la navigation (seulement si le plugin existe)
|
||||
if (config.plugins.has('preload')) {
|
||||
config.plugin('preload')
|
||||
.tap(args => {
|
||||
args[0].include = 'initial';
|
||||
return args;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue