@bobdivx/mf3d v1.0.3
Filament Manager
Gestion des traductions
Le projet utilise un système de traduction basé sur TypeScript pour gérer le contenu multilingue. Les traductions sont stockées dans le dossier src/i18n/locales/.
Structure des traductions
src/i18n/
├── locales/
│ ├── en/ # Dossier des traductions anglaises
│ │ ├── stats.ts
│ │ ├── filaments.ts
│ │ └── ...
│ ├── fr/ # Dossier des traductions françaises
│ │ ├── stats.ts
│ │ ├── filaments.ts
│ │ └── ...
│ ├── en.ts # Fichier principal des traductions anglaises
│ └── fr.ts # Fichier principal des traductions françaises
├── types.ts # Types pour les traductions
├── config.ts # Configuration et utilitaires de traduction
└── utils.ts # Utilitaires (getLanguageFromURL, etc.)Organisation des traductions
Les traductions sont organisées par modules fonctionnels. Par exemple, pour les statistiques :
// src/i18n/locales/fr/stats.ts
export const stats = {
title: "Statistiques",
description: "Statistiques des filaments",
// Statistiques générales
total_filaments: "Nombre total de filaments",
total_weight: "Poids total",
// Actions
actions: {
refresh: "Rafraîchir",
print: "Imprimer",
},
// Sous-sections
filaments_stats: {
title: "Filaments",
// ...
},
prints_stats: {
title: "Impressions",
// ...
},
};Procédure d'utilisation des traductions
1. Dans une page Astro
Imports nécessaires :
--- import Layout from '@/layouts/Layout.astro'; import { getLangFromUrl, useTranslations } from '@/i18n/config'; ---Récupération de la langue et utilisation des traductions :
--- const lang = getLangFromUrl(Astro.url); const t = useTranslations(lang); ---Structure de la page :
<Layout title={t('stats.title')} description={t('stats.description')} lang={lang}> <div> <h1>{t('stats.title')}</h1> <p>{t('stats.description')}</p> </div> </Layout>
2. Dans un composant Preact
Import du hook :
import { useTranslations } from "@/i18n/config";Utilisation dans le composant :
interface Props { lang: Language; } export default function MonComposant({ lang }: Props) { const t = useTranslations(lang); return ( <div> <h1>{t("stats.title")}</h1> <p>{t("stats.description")}</p> </div> ); }
Ajout de nouvelles traductions
Structure des fichiers de traduction :
- Chaque module (ex: stats, filaments) a son propre fichier dans les dossiers
en/etfr/ - Les fichiers principaux
en.tsetfr.tsimportent et agrègent tous les modules - Toujours ajouter les traductions dans les deux langues (FR et EN)
- Chaque module (ex: stats, filaments) a son propre fichier dans les dossiers
Exemple d'ajout d'une nouvelle section :
// Dans src/i18n/locales/fr/stats.ts export const stats = { // ... traductions existantes nouvelle_section: { title: "Titre de la section", description: "Description de la section", // ... autres traductions }, }; // Dans src/i18n/locales/en/stats.ts export const stats = { // ... existing translations nouvelle_section: { title: "Section title", description: "Section description", // ... other translations }, };Bonnes pratiques :
- Utiliser des clés descriptives et cohérentes
- Organiser les traductions de manière hiérarchique
- Maintenir la même structure dans toutes les langues
- Vérifier que toutes les clés existent dans toutes les langues
- Utiliser des valeurs appropriées pour chaque langue
Utilisation des traductions dans le code
Accès simple :
t("stats.title");Avec des variables :
`${value} ${t("stats.unit")}`;Pour les actions :
const actions = { label: t("stats.actions.refresh"), onClickId: "refresh-stats", };
Maintenance des traductions
Vérification des clés manquantes :
- S'assurer que chaque clé existe dans toutes les langues
- Maintenir la même structure hiérarchique
Mise à jour des traductions :
- Modifier les fichiers de traduction correspondants
- Tester l'affichage dans toutes les langues
- Vérifier la cohérence des traductions
Tests :
- Vérifier l'affichage dans l'interface
- Tester le changement de langue
- Valider les traductions avec les utilisateurs
Design System
Page Layout
Chaque page principale suit une structure cohérente :
Bandeau d'en-tête (Header Banner)
- Dégradé de couleur personnalisable via les variables CSS
- Titre principal et sous-titre
- Actions principales
- Classes :
header-banner,header-banner-content,header-banner-title
Sections de contenu
- Cartes avec ombres et coins arrondis
- Icônes dans des cercles avec couleurs pastels
- Classes :
section-card,section-card-title
Composants
Icônes
- Toujours affichées dans des cercles
- Tailles standardisées :
- Grande :
section-icon(40x40px) - Petite :
section-icon-sm(32x32px)
- Grande :
- Couleurs pastels prédéfinies :
section-icon-bluesection-icon-purplesection-icon-greensection-icon-yellowsection-icon-amber
Boutons
- Style principal :
header-banner-button - Avec icône :
header-banner-button-icon
- Style principal :
Thème et Personnalisation
Les couleurs du bandeau sont personnalisables via les variables CSS :
:root {
--banner-from-color: theme("colors.blue.500");
--banner-to-color: theme("colors.purple.600");
}Pour le thème sombre :
[data-theme="dark"] {
--banner-from-color: theme("colors.blue.600");
--banner-to-color: theme("colors.purple.700");
}Utilisation
Exemple de structure de page :
<div class="header-banner">
<div class="header-banner-content">
<h1 class="header-banner-title">Titre de la page</h1>
<p class="header-banner-subtitle">Description</p>
</div>
</div>
<div class="section-card">
<h2 class="section-card-title">
<div class="section-icon section-icon-blue">
<Icon />
</div>
Titre de section
</h2>
<!-- Contenu -->
</div>Gestion des types pour les traductions
Types de base :
- Les traductions retournent un type
TranslationValuequi peut être soit unestringsoit une fonction retournant unestring - Pour convertir une traduction en string, utilisez la fonction
getTranslationString():
import { getTranslationString } from "@/lib/utils/translation"; // Dans un composant const t = useTranslations(lang); const title = getTranslationString(t("page.title"));- Les traductions retournent un type
Utilisation dans les composants :
- Pour les props qui attendent une string, utilisez toujours
getTranslationString():
<Button disabled={isLoading}> {getTranslationString(t('common.save'))} </Button>- Pour les props qui attendent une string, utilisez toujours
Bonnes pratiques :
- Utilisez
getTranslationString()pour les props qui attendent une string - Pour les textes affichés directement dans le JSX, vous pouvez utiliser directement
t() - Vérifiez les types TypeScript pour éviter les erreurs de compilation
- Utilisez
Gestion des stocks de filaments
Le système de gestion des stocks de filaments permet d'ajouter et de retirer des bobines entières de filament, en tenant compte du poids défini pour chaque type de filament.
Structure du système
Composants principaux :
SpoolManager: Interface utilisateur pour gérer le stockTargetSpoolsManager: Interface pour définir le stock cibleFilamentHistory: Affichage de l'historique des modifications
API :
/api/filaments/[id]/spool: Endpoint pour ajouter/retirer du stock/api/filaments/[id]/target: Endpoint pour définir le stock cible
Fonctionnement du SpoolManager
Le composant SpoolManager affiche l'état actuel du stock et permet d'ajouter ou de retirer des bobines complètes.
<SpoolManager
filamentId={filament.id}
currentStock={remainingWeight}
targetStock={targetTotalWeight}
_lang={lang}
client:load
/>Propriétés importantes :
filamentId: Identifiant unique du filamentcurrentStock: Poids actuel restant en grammestargetStock: Poids cible total en grammes_lang: Langue courante pour les traductions
Caractéristiques :
Affichage :
- Poids en kilogrammes (pour plus de lisibilité)
- Nombre de bobines actuelles et cibles
- Pourcentage du stock par rapport à l'objectif
- Radar visuel montrant la proportion du stock
Actions :
- Bouton pour ajouter une bobine entière
- Bouton pour retirer une bobine entière
- Validations pour empêcher le stock négatif
API de gestion du stock
L'API /api/filaments/[id]/spool est conçue pour gérer les ajouts et retraits de stock.
Requête :
// Exemple d'appel pour ajouter du stock
fetch(`/api/filaments/${filamentId}/spool`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ weight: 1000 }), // Poids positif pour ajouter
});
// Exemple d'appel pour retirer du stock
fetch(`/api/filaments/${filamentId}/spool`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ weight: -1000 }), // Poids négatif pour retirer
});Fonctionnement interne :
Récupération du contexte :
- Poids actuel du filament (
remaining_weight) - Poids d'une bobine individuelle (
weight)
- Poids actuel du filament (
Calcul du nouveau poids :
- Addition du poids fourni au poids actuel
- Validation que le résultat n'est pas négatif
Mise à jour de la base de données :
- Mise à jour du champ
remaining_weight - Ajout d'une entrée dans l'historique avec l'opération et la quantité
- Mise à jour du champ
Réponse :
{ "success": true, "previousWeight": 1000, "newWeight": 2000, "spoolWeight": 1000, "difference": 1000, "totalSpools": 2 }
Historique des modifications
Chaque modification de stock est enregistrée dans la table filament_history avec les informations suivantes :
filament_id: ID du filament concernéoperation: Type d'opération ('add' ou 'remove')quantity: Quantité ajoutée ou retirée (toujours positive)user_id: ID de l'utilisateur ayant effectué l'opérationcreated_at: Date et heure de l'opération
Bonnes pratiques
Opérations par bobines entières :
- Toujours ajouter ou retirer des bobines entières
- Le poids d'une bobine est défini dans la base de données pour chaque filament
Validation des données :
- Vérifier que le stock ne devient pas négatif
- Valider les entrées utilisateur
- Utiliser des valeurs par défaut appropriées
Affichage des informations :
- Afficher les poids en kilogrammes (plus lisible)
- Afficher également le nombre de bobines
- Utiliser des indicateurs visuels (comme le radar de stock)
Gestion des erreurs :
- Afficher des messages d'erreur clairs
- Utiliser le composant Toast pour les notifications
- Logger les erreurs pour le débogage
Exemple complet
// Composant SpoolManager (version simplifiée)
function SpoolManager({ filamentId, currentStock, targetStock, _lang }) {
const t = useTranslations(_lang);
const handleUpdateStock = async (action) => {
try {
const weightChange = action === "add" ? spoolWeight : -spoolWeight;
await fetch(`/api/filaments/${filamentId}/spool`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ weight: weightChange }),
});
// Notification et rafraîchissement
} catch (error) {
// Gestion des erreurs
}
};
return (
<div>
{/* Affichage du stock */}
<div>
{currentStockKg} kg ({currentSpools} bobines)
</div>
{/* Boutons d'action */}
<Button onClick={() => handleUpdateStock("add")}>
Ajouter une bobine
</Button>
<Button onClick={() => handleUpdateStock("remove")}>
Retirer une bobine
</Button>
</div>
);
}Authentification et Comptes de Test
Le système d'authentification utilise bcrypt pour le hachage sécurisé des mots de passe et des sessions basées sur des tokens.
Comptes de Test Disponibles
Deux comptes de test sont disponibles pour le développement :
Compte Administrateur
- Email : bobdivx@gmail.com
- Mot de passe : 8tc6vr89
- Rôle : admin
Compte Utilisateur Test
- Email : test@exemple.com
- Mot de passe : password
- Rôle : user
Processus d'Authentification
Connexion
- Les identifiants sont envoyés à
/api/auth/login - Le serveur vérifie l'existence de l'utilisateur
- Le mot de passe est vérifié avec bcrypt
- Une session est créée avec un token unique
- Un cookie sécurisé est défini avec le token
- Les identifiants sont envoyés à
Sécurité
- Les mots de passe sont hachés avec bcrypt (10 rounds)
- Les sessions expirent après 7 jours
- Les cookies sont configurés avec :
- HttpOnly
- SameSite=Lax
- Secure en production
- Path=/
Déconnexion
- La session est supprimée de la base de données
- Le cookie est expiré
Dépannage de l'Authentification
Si vous rencontrez des problèmes de connexion :
Vérifiez les logs
- Les logs détaillés sont disponibles dans la console du navigateur
- Les logs serveur montrent le processus d'authentification
Vérifiez les cookies
- Le cookie
turso-tokendoit être présent après la connexion - Les options du cookie doivent correspondre à la configuration
- Le cookie
Vérifiez la base de données
- L'utilisateur doit exister dans la table
auth_users - Le mot de passe doit être correctement haché
- La session doit être créée dans la table
sessions
- L'utilisateur doit exister dans la table