Remove next-intl localization infrastructure
Build and Push Docker Image / build (push) Successful in 16m25s Details

Strips out the i18n layer (next-intl, message files, language switcher,
provider) to reduce complexity. Nav components now use plain English strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt 2026-02-11 14:57:27 +01:00
parent ce4069bf92
commit bd9cd310fc
14 changed files with 25 additions and 1488 deletions

View File

@ -1,327 +0,0 @@
{
"common": {
"loading": "Loading...",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"create": "Create",
"close": "Close",
"confirm": "Confirm",
"back": "Back",
"next": "Next",
"submit": "Submit",
"search": "Search",
"filter": "Filter",
"export": "Export",
"import": "Import",
"refresh": "Refresh",
"actions": "Actions",
"status": "Status",
"name": "Name",
"email": "Email",
"role": "Role",
"date": "Date",
"description": "Description",
"settings": "Settings",
"yes": "Yes",
"no": "No",
"all": "All",
"none": "None",
"noResults": "No results found",
"error": "Error",
"success": "Success",
"warning": "Warning",
"info": "Info",
"required": "Required",
"optional": "Optional",
"total": "Total",
"page": "Page",
"of": "of",
"showing": "Showing",
"entries": "entries",
"perPage": "per page",
"language": "Language",
"english": "English",
"french": "French"
},
"auth": {
"signIn": "Sign In",
"signOut": "Sign Out",
"signUp": "Sign Up",
"email": "Email Address",
"password": "Password",
"forgotPassword": "Forgot Password?",
"resetPassword": "Reset Password",
"newPassword": "New Password",
"confirmPassword": "Confirm Password",
"currentPassword": "Current Password",
"magicLink": "Sign in with Magic Link",
"magicLinkSent": "Check your email for a sign-in link",
"invalidCredentials": "Invalid email or password",
"accountLocked": "Account locked. Try again later.",
"setPassword": "Set Password",
"passwordRequirements": "Password must be at least 8 characters",
"passwordsDoNotMatch": "Passwords do not match",
"welcomeBack": "Welcome back",
"signInDescription": "Enter your email to sign in to your account",
"orContinueWith": "Or continue with"
},
"nav": {
"dashboard": "Dashboard",
"programs": "Programs",
"rounds": "Rounds",
"projects": "Projects",
"users": "Users",
"evaluations": "Evaluations",
"assignments": "Assignments",
"analytics": "Analytics",
"settings": "Settings",
"audit": "Audit Log",
"myProjects": "My Projects",
"myEvaluations": "My Evaluations",
"profile": "Profile",
"help": "Help",
"notifications": "Notifications",
"mentoring": "Mentoring",
"liveVoting": "Live Voting",
"applications": "Applications",
"messages": "Messages",
"team": "Team",
"documents": "Documents",
"awards": "Awards",
"compare": "Compare",
"learningHub": "Learning Hub",
"reports": "Reports"
},
"dashboard": {
"title": "Dashboard",
"welcome": "Welcome, {name}",
"overview": "Overview",
"recentActivity": "Recent Activity",
"pendingEvaluations": "Pending Evaluations",
"completedEvaluations": "Completed Evaluations",
"totalProjects": "Total Projects",
"activeRounds": "Active Rounds",
"assignedProjects": "Assigned Projects",
"upcomingDeadlines": "Upcoming Deadlines",
"quickActions": "Quick Actions",
"noActivity": "No recent activity"
},
"programs": {
"title": "Programs",
"createProgram": "Create Program",
"editProgram": "Edit Program",
"programName": "Program Name",
"year": "Year",
"status": "Status",
"rounds": "Rounds",
"projects": "Projects",
"noPrograms": "No programs found",
"deleteConfirm": "Are you sure you want to delete this program?"
},
"rounds": {
"title": "Rounds",
"createRound": "Create Round",
"editRound": "Edit Round",
"roundName": "Round Name",
"roundType": "Round Type",
"startDate": "Start Date",
"endDate": "End Date",
"votingWindow": "Voting Window",
"criteria": "Evaluation Criteria",
"status": "Status",
"active": "Active",
"closed": "Closed",
"upcoming": "Upcoming",
"noRounds": "No rounds found"
},
"projects": {
"title": "Projects",
"createProject": "Create Project",
"editProject": "Edit Project",
"projectName": "Project Title",
"teamName": "Team Name",
"country": "Country",
"category": "Category",
"status": "Status",
"description": "Description",
"files": "Files",
"evaluations": "Evaluations",
"noProjects": "No projects found",
"importCsv": "Import CSV",
"bulkStatusUpdate": "Bulk Status Update",
"viewDetails": "View Details",
"assignMentor": "Assign Mentor",
"oceanIssue": "Ocean Issue"
},
"evaluations": {
"title": "Evaluations",
"submitEvaluation": "Submit Evaluation",
"draft": "Draft",
"submitted": "Submitted",
"score": "Score",
"feedback": "Feedback",
"criteria": "Criteria",
"globalScore": "Global Score",
"decision": "Decision",
"recommend": "Recommend",
"doNotRecommend": "Do Not Recommend",
"saveAsDraft": "Save as Draft",
"finalSubmit": "Final Submit",
"confirmSubmit": "Are you sure? This action cannot be undone.",
"progress": "Progress",
"completionRate": "Completion Rate",
"noEvaluations": "No evaluations yet",
"evaluationSummary": "Evaluation Summary",
"strengths": "Strengths",
"weaknesses": "Weaknesses",
"overallAssessment": "Overall Assessment"
},
"users": {
"title": "Users",
"createUser": "Create User",
"editUser": "Edit User",
"inviteUser": "Invite User",
"bulkImport": "Bulk Import",
"sendInvitation": "Send Invitation",
"resendInvitation": "Resend Invitation",
"role": "Role",
"status": "Status",
"active": "Active",
"invited": "Invited",
"suspended": "Suspended",
"noUsers": "No users found",
"expertiseTags": "Expertise Tags",
"maxAssignments": "Max Assignments",
"lastLogin": "Last Login",
"deleteConfirm": "Are you sure you want to delete this user?"
},
"assignments": {
"title": "Assignments",
"assign": "Assign",
"unassign": "Unassign",
"bulkAssign": "Bulk Assign",
"smartAssign": "Smart Assignment",
"manual": "Manual",
"algorithm": "Algorithm",
"aiAuto": "AI Auto",
"noAssignments": "No assignments",
"assignedTo": "Assigned to",
"assignedBy": "Assigned by"
},
"files": {
"title": "Files",
"upload": "Upload File",
"download": "Download",
"delete": "Delete File",
"fileName": "File Name",
"fileType": "File Type",
"fileSize": "File Size",
"uploadDate": "Upload Date",
"noFiles": "No files uploaded",
"dragAndDrop": "Drag and drop files here",
"maxSize": "Maximum file size: {size}",
"version": "Version",
"versionHistory": "Version History",
"replaceFile": "Replace File",
"bulkDownload": "Bulk Download"
},
"settings": {
"title": "Settings",
"general": "General",
"branding": "Branding",
"email": "Email",
"security": "Security",
"ai": "AI Configuration",
"storage": "Storage",
"language": "Language",
"defaultLanguage": "Default Language",
"availableLanguages": "Available Languages",
"languageDescription": "Configure the platform's language settings",
"saved": "Settings saved successfully",
"saveFailed": "Failed to save settings"
},
"liveVoting": {
"title": "Live Voting",
"session": "Session",
"startVoting": "Start Voting",
"stopVoting": "Stop Voting",
"endSession": "End Session",
"timeRemaining": "Time Remaining",
"castVote": "Cast Your Vote",
"voteSubmitted": "Vote submitted",
"results": "Results",
"juryScore": "Jury Score",
"audienceScore": "Audience Score",
"weightedTotal": "Weighted Total",
"noVotes": "No votes yet",
"votingClosed": "Voting has closed",
"presentationSettings": "Presentation Settings",
"audienceVoting": "Audience Voting"
},
"mentor": {
"title": "Mentoring",
"myMentees": "My Mentees",
"projectDetails": "Project Details",
"sendMessage": "Send Message",
"notes": "Notes",
"addNote": "Add Note",
"milestones": "Milestones",
"completeMilestone": "Mark Complete",
"activity": "Activity",
"lastViewed": "Last Viewed",
"noMentees": "No mentees assigned"
},
"profile": {
"title": "Profile",
"editProfile": "Edit Profile",
"name": "Full Name",
"email": "Email",
"phone": "Phone Number",
"country": "Country",
"bio": "Bio",
"expertise": "Areas of Expertise",
"notifications": "Notification Preferences",
"digestFrequency": "Digest Frequency",
"availability": "Availability",
"workload": "Preferred Workload",
"changePassword": "Change Password",
"deleteAccount": "Delete Account",
"deleteAccountConfirm": "This action cannot be undone. All your data will be permanently deleted."
},
"onboarding": {
"welcome": "Welcome to MOPC",
"setupProfile": "Let's set up your profile",
"step1": "Personal Information",
"step2": "Expertise & Preferences",
"step3": "Review & Complete",
"complete": "Complete Setup",
"skip": "Skip for now"
},
"errors": {
"generic": "Something went wrong. Please try again.",
"notFound": "Page not found",
"unauthorized": "You are not authorized to access this page",
"forbidden": "Access denied",
"serverError": "Internal server error",
"networkError": "Network error. Please check your connection.",
"sessionExpired": "Your session has expired. Please sign in again.",
"validationError": "Please check the form for errors"
},
"notifications": {
"title": "Notifications",
"markAllRead": "Mark all as read",
"noNotifications": "No notifications",
"viewAll": "View all notifications"
},
"coi": {
"title": "Conflict of Interest",
"declaration": "COI Declaration",
"declareConflict": "Declare Conflict of Interest",
"noConflict": "No conflict of interest",
"hasConflict": "Conflict declared",
"reason": "Reason for conflict",
"confirmDeclaration": "I confirm this declaration is accurate"
}
}

View File

@ -1,327 +0,0 @@
{
"common": {
"loading": "Chargement...",
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer",
"edit": "Modifier",
"create": "Cr\u00e9er",
"close": "Fermer",
"confirm": "Confirmer",
"back": "Retour",
"next": "Suivant",
"submit": "Soumettre",
"search": "Rechercher",
"filter": "Filtrer",
"export": "Exporter",
"import": "Importer",
"refresh": "Actualiser",
"actions": "Actions",
"status": "Statut",
"name": "Nom",
"email": "E-mail",
"role": "R\u00f4le",
"date": "Date",
"description": "Description",
"settings": "Param\u00e8tres",
"yes": "Oui",
"no": "Non",
"all": "Tous",
"none": "Aucun",
"noResults": "Aucun r\u00e9sultat trouv\u00e9",
"error": "Erreur",
"success": "Succ\u00e8s",
"warning": "Avertissement",
"info": "Information",
"required": "Obligatoire",
"optional": "Facultatif",
"total": "Total",
"page": "Page",
"of": "de",
"showing": "Affichage de",
"entries": "entr\u00e9es",
"perPage": "par page",
"language": "Langue",
"english": "Anglais",
"french": "Fran\u00e7ais"
},
"auth": {
"signIn": "Se connecter",
"signOut": "Se d\u00e9connecter",
"signUp": "S'inscrire",
"email": "Adresse e-mail",
"password": "Mot de passe",
"forgotPassword": "Mot de passe oubli\u00e9 ?",
"resetPassword": "R\u00e9initialiser le mot de passe",
"newPassword": "Nouveau mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"currentPassword": "Mot de passe actuel",
"magicLink": "Se connecter avec un lien magique",
"magicLinkSent": "V\u00e9rifiez votre e-mail pour un lien de connexion",
"invalidCredentials": "E-mail ou mot de passe invalide",
"accountLocked": "Compte verrouill\u00e9. R\u00e9essayez plus tard.",
"setPassword": "D\u00e9finir le mot de passe",
"passwordRequirements": "Le mot de passe doit contenir au moins 8 caract\u00e8res",
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas",
"welcomeBack": "Bon retour",
"signInDescription": "Entrez votre e-mail pour vous connecter \u00e0 votre compte",
"orContinueWith": "Ou continuer avec"
},
"nav": {
"dashboard": "Tableau de bord",
"programs": "Programmes",
"rounds": "Tours",
"projects": "Projets",
"users": "Utilisateurs",
"evaluations": "\u00c9valuations",
"assignments": "Affectations",
"analytics": "Analytique",
"settings": "Param\u00e8tres",
"audit": "Journal d'audit",
"myProjects": "Mes projets",
"myEvaluations": "Mes \u00e9valuations",
"profile": "Profil",
"help": "Aide",
"notifications": "Notifications",
"mentoring": "Mentorat",
"liveVoting": "Vote en direct",
"applications": "Candidatures",
"messages": "Messages",
"team": "\u00c9quipe",
"documents": "Documents",
"awards": "Prix",
"compare": "Comparer",
"learningHub": "Centre de ressources",
"reports": "Rapports"
},
"dashboard": {
"title": "Tableau de bord",
"welcome": "Bienvenue, {name}",
"overview": "Aper\u00e7u",
"recentActivity": "Activit\u00e9 r\u00e9cente",
"pendingEvaluations": "\u00c9valuations en attente",
"completedEvaluations": "\u00c9valuations termin\u00e9es",
"totalProjects": "Total des projets",
"activeRounds": "Tours actifs",
"assignedProjects": "Projets assign\u00e9s",
"upcomingDeadlines": "\u00c9ch\u00e9ances \u00e0 venir",
"quickActions": "Actions rapides",
"noActivity": "Aucune activit\u00e9 r\u00e9cente"
},
"programs": {
"title": "Programmes",
"createProgram": "Cr\u00e9er un programme",
"editProgram": "Modifier le programme",
"programName": "Nom du programme",
"year": "Ann\u00e9e",
"status": "Statut",
"rounds": "Tours",
"projects": "Projets",
"noPrograms": "Aucun programme trouv\u00e9",
"deleteConfirm": "\u00cates-vous s\u00fbr de vouloir supprimer ce programme ?"
},
"rounds": {
"title": "Tours",
"createRound": "Cr\u00e9er un tour",
"editRound": "Modifier le tour",
"roundName": "Nom du tour",
"roundType": "Type de tour",
"startDate": "Date de d\u00e9but",
"endDate": "Date de fin",
"votingWindow": "Fen\u00eatre de vote",
"criteria": "Crit\u00e8res d'\u00e9valuation",
"status": "Statut",
"active": "Actif",
"closed": "Cl\u00f4tur\u00e9",
"upcoming": "\u00c0 venir",
"noRounds": "Aucun tour trouv\u00e9"
},
"projects": {
"title": "Projets",
"createProject": "Cr\u00e9er un projet",
"editProject": "Modifier le projet",
"projectName": "Titre du projet",
"teamName": "Nom de l'\u00e9quipe",
"country": "Pays",
"category": "Cat\u00e9gorie",
"status": "Statut",
"description": "Description",
"files": "Fichiers",
"evaluations": "\u00c9valuations",
"noProjects": "Aucun projet trouv\u00e9",
"importCsv": "Importer CSV",
"bulkStatusUpdate": "Mise \u00e0 jour en masse du statut",
"viewDetails": "Voir les d\u00e9tails",
"assignMentor": "Assigner un mentor",
"oceanIssue": "Probl\u00e9matique oc\u00e9anique"
},
"evaluations": {
"title": "\u00c9valuations",
"submitEvaluation": "Soumettre l'\u00e9valuation",
"draft": "Brouillon",
"submitted": "Soumis",
"score": "Note",
"feedback": "Commentaires",
"criteria": "Crit\u00e8res",
"globalScore": "Note globale",
"decision": "D\u00e9cision",
"recommend": "Recommander",
"doNotRecommend": "Ne pas recommander",
"saveAsDraft": "Enregistrer comme brouillon",
"finalSubmit": "Soumission finale",
"confirmSubmit": "\u00cates-vous s\u00fbr ? Cette action est irr\u00e9versible.",
"progress": "Progression",
"completionRate": "Taux de compl\u00e9tion",
"noEvaluations": "Aucune \u00e9valuation pour le moment",
"evaluationSummary": "R\u00e9sum\u00e9 de l'\u00e9valuation",
"strengths": "Points forts",
"weaknesses": "Points faibles",
"overallAssessment": "\u00c9valuation globale"
},
"users": {
"title": "Utilisateurs",
"createUser": "Cr\u00e9er un utilisateur",
"editUser": "Modifier l'utilisateur",
"inviteUser": "Inviter un utilisateur",
"bulkImport": "Importation en masse",
"sendInvitation": "Envoyer l'invitation",
"resendInvitation": "Renvoyer l'invitation",
"role": "R\u00f4le",
"status": "Statut",
"active": "Actif",
"invited": "Invit\u00e9",
"suspended": "Suspendu",
"noUsers": "Aucun utilisateur trouv\u00e9",
"expertiseTags": "Tags d'expertise",
"maxAssignments": "Affectations max.",
"lastLogin": "Derni\u00e8re connexion",
"deleteConfirm": "\u00cates-vous s\u00fbr de vouloir supprimer cet utilisateur ?"
},
"assignments": {
"title": "Affectations",
"assign": "Affecter",
"unassign": "D\u00e9saffecter",
"bulkAssign": "Affectation en masse",
"smartAssign": "Affectation intelligente",
"manual": "Manuel",
"algorithm": "Algorithme",
"aiAuto": "IA automatique",
"noAssignments": "Aucune affectation",
"assignedTo": "Affect\u00e9 \u00e0",
"assignedBy": "Affect\u00e9 par"
},
"files": {
"title": "Fichiers",
"upload": "T\u00e9l\u00e9charger un fichier",
"download": "T\u00e9l\u00e9charger",
"delete": "Supprimer le fichier",
"fileName": "Nom du fichier",
"fileType": "Type de fichier",
"fileSize": "Taille du fichier",
"uploadDate": "Date de t\u00e9l\u00e9chargement",
"noFiles": "Aucun fichier t\u00e9l\u00e9charg\u00e9",
"dragAndDrop": "Glissez-d\u00e9posez les fichiers ici",
"maxSize": "Taille maximale du fichier : {size}",
"version": "Version",
"versionHistory": "Historique des versions",
"replaceFile": "Remplacer le fichier",
"bulkDownload": "T\u00e9l\u00e9chargement en masse"
},
"settings": {
"title": "Param\u00e8tres",
"general": "G\u00e9n\u00e9ral",
"branding": "Image de marque",
"email": "E-mail",
"security": "S\u00e9curit\u00e9",
"ai": "Configuration IA",
"storage": "Stockage",
"language": "Langue",
"defaultLanguage": "Langue par d\u00e9faut",
"availableLanguages": "Langues disponibles",
"languageDescription": "Configurer les param\u00e8tres de langue de la plateforme",
"saved": "Param\u00e8tres enregistr\u00e9s avec succ\u00e8s",
"saveFailed": "\u00c9chec de l'enregistrement des param\u00e8tres"
},
"liveVoting": {
"title": "Vote en direct",
"session": "Session",
"startVoting": "D\u00e9marrer le vote",
"stopVoting": "Arr\u00eater le vote",
"endSession": "Terminer la session",
"timeRemaining": "Temps restant",
"castVote": "Voter",
"voteSubmitted": "Vote soumis",
"results": "R\u00e9sultats",
"juryScore": "Note du jury",
"audienceScore": "Note du public",
"weightedTotal": "Total pond\u00e9r\u00e9",
"noVotes": "Aucun vote pour le moment",
"votingClosed": "Le vote est clos",
"presentationSettings": "Param\u00e8tres de pr\u00e9sentation",
"audienceVoting": "Vote du public"
},
"mentor": {
"title": "Mentorat",
"myMentees": "Mes mentees",
"projectDetails": "D\u00e9tails du projet",
"sendMessage": "Envoyer un message",
"notes": "Notes",
"addNote": "Ajouter une note",
"milestones": "Jalons",
"completeMilestone": "Marquer comme termin\u00e9",
"activity": "Activit\u00e9",
"lastViewed": "Derni\u00e8re consultation",
"noMentees": "Aucun mentee assign\u00e9"
},
"profile": {
"title": "Profil",
"editProfile": "Modifier le profil",
"name": "Nom complet",
"email": "E-mail",
"phone": "Num\u00e9ro de t\u00e9l\u00e9phone",
"country": "Pays",
"bio": "Biographie",
"expertise": "Domaines d'expertise",
"notifications": "Pr\u00e9f\u00e9rences de notification",
"digestFrequency": "Fr\u00e9quence du digest",
"availability": "Disponibilit\u00e9",
"workload": "Charge de travail pr\u00e9f\u00e9r\u00e9e",
"changePassword": "Changer le mot de passe",
"deleteAccount": "Supprimer le compte",
"deleteAccountConfirm": "Cette action est irr\u00e9versible. Toutes vos donn\u00e9es seront d\u00e9finitivement supprim\u00e9es."
},
"onboarding": {
"welcome": "Bienvenue sur MOPC",
"setupProfile": "Configurons votre profil",
"step1": "Informations personnelles",
"step2": "Expertise et pr\u00e9f\u00e9rences",
"step3": "V\u00e9rification et finalisation",
"complete": "Terminer la configuration",
"skip": "Passer pour le moment"
},
"errors": {
"generic": "Une erreur s'est produite. Veuillez r\u00e9essayer.",
"notFound": "Page non trouv\u00e9e",
"unauthorized": "Vous n'\u00eates pas autoris\u00e9 \u00e0 acc\u00e9der \u00e0 cette page",
"forbidden": "Acc\u00e8s refus\u00e9",
"serverError": "Erreur interne du serveur",
"networkError": "Erreur r\u00e9seau. V\u00e9rifiez votre connexion.",
"sessionExpired": "Votre session a expir\u00e9. Veuillez vous reconnecter.",
"validationError": "Veuillez v\u00e9rifier le formulaire pour les erreurs"
},
"notifications": {
"title": "Notifications",
"markAllRead": "Tout marquer comme lu",
"noNotifications": "Aucune notification",
"viewAll": "Voir toutes les notifications"
},
"coi": {
"title": "Conflit d'int\u00e9r\u00eats",
"declaration": "D\u00e9claration de COI",
"declareConflict": "D\u00e9clarer un conflit d'int\u00e9r\u00eats",
"noConflict": "Aucun conflit d'int\u00e9r\u00eats",
"hasConflict": "Conflit d\u00e9clar\u00e9",
"reason": "Raison du conflit",
"confirmDeclaration": "Je confirme que cette d\u00e9claration est exacte"
}
}

View File

@ -1,5 +1,4 @@
import type { NextConfig } from 'next' import type { NextConfig } from 'next'
import createNextIntlPlugin from 'next-intl/plugin'
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: 'standalone', output: 'standalone',
@ -18,6 +17,4 @@ const nextConfig: NextConfig = {
}, },
} }
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts') export default nextConfig
export default withNextIntl(nextConfig)

703
package-lock.json generated
View File

@ -57,7 +57,6 @@
"motion": "^11.15.0", "motion": "^11.15.0",
"next": "^15.1.0", "next": "^15.1.0",
"next-auth": "^5.0.0-beta.25", "next-auth": "^5.0.0-beta.25",
"next-intl": "^4.8.2",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nodemailer": "^7.0.7", "nodemailer": "^7.0.7",
"openai": "^6.16.0", "openai": "^6.16.0",
@ -1004,67 +1003,6 @@
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@formatjs/ecma402-abstract": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-3.1.1.tgz",
"integrity": "sha512-jhZbTwda+2tcNrs4kKvxrPLPjx8QsBCLCUgrrJ/S+G9YrGHWLhAyFMMBHJBnBoOwuLHd7L14FgYudviKaxkO2Q==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "3.1.0",
"@formatjs/intl-localematcher": "0.8.1",
"decimal.js": "^10.6.0",
"tslib": "^2.8.1"
}
},
"node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.1.tgz",
"integrity": "sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "3.1.0",
"tslib": "^2.8.1"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.0.tgz",
"integrity": "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.8.1"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.1.tgz",
"integrity": "sha512-sSDmSvmmoVQ92XqWb499KrIhv/vLisJU8ITFrx7T7NZHUmMY7EL9xgRowAosaljhqnj/5iufG24QrdzB6X3ItA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "3.1.1",
"@formatjs/icu-skeleton-parser": "2.1.1",
"tslib": "^2.8.1"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.1.tgz",
"integrity": "sha512-PSFABlcNefjI6yyk8f7nyX1DC7NHmq6WaCHZLySEXBrXuLOB2f935YsnzuPjlz+ibhb9yWTdPeVX1OVcj24w2Q==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "3.1.1",
"tslib": "^2.8.1"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz",
"integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==",
"license": "MIT",
"dependencies": {
"tslib": "2"
}
},
"node_modules/@handlewithcare/prosemirror-inputrules": { "node_modules/@handlewithcare/prosemirror-inputrules": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/@handlewithcare/prosemirror-inputrules/-/prosemirror-inputrules-0.1.4.tgz", "resolved": "https://registry.npmjs.org/@handlewithcare/prosemirror-inputrules/-/prosemirror-inputrules-0.1.4.tgz",
@ -1916,301 +1854,6 @@
"url": "https://github.com/sponsors/panva" "url": "https://github.com/sponsors/panva"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
"node-addon-api": "^7.0.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.6",
"@parcel/watcher-darwin-arm64": "2.5.6",
"@parcel/watcher-darwin-x64": "2.5.6",
"@parcel/watcher-freebsd-x64": "2.5.6",
"@parcel/watcher-linux-arm-glibc": "2.5.6",
"@parcel/watcher-linux-arm-musl": "2.5.6",
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
"@parcel/watcher-linux-arm64-musl": "2.5.6",
"@parcel/watcher-linux-x64-glibc": "2.5.6",
"@parcel/watcher-linux-x64-musl": "2.5.6",
"@parcel/watcher-win32-arm64": "2.5.6",
"@parcel/watcher-win32-ia32": "2.5.6",
"@parcel/watcher-win32-x64": "2.5.6"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
"integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
"integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
"integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
"integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
"integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
"integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
"integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
"integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
"integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
"integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
"integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
"integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
"integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@playwright/test": { "node_modules/@playwright/test": {
"version": "1.58.0", "version": "1.58.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.0.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.0.tgz",
@ -4080,12 +3723,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@schummar/icu-type-parser": {
"version": "1.21.5",
"resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
"license": "MIT"
},
"node_modules/@shikijs/types": { "node_modules/@shikijs/types": {
"version": "3.21.0", "version": "3.21.0",
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.21.0.tgz", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.21.0.tgz",
@ -4114,172 +3751,6 @@
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@swc/core-darwin-arm64": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz",
"integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz",
"integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz",
"integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz",
"integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz",
"integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz",
"integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz",
"integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz",
"integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz",
"integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==",
"cpu": [
"ia32"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz",
"integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
"license": "Apache-2.0"
},
"node_modules/@swc/helpers": { "node_modules/@swc/helpers": {
"version": "0.5.15", "version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@ -4289,15 +3760,6 @@
"tslib": "^2.8.0" "tslib": "^2.8.0"
} }
}, },
"node_modules/@swc/types": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
"integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3"
}
},
"node_modules/@tailwindcss/node": { "node_modules/@tailwindcss/node": {
"version": "4.1.18", "version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
@ -6838,12 +6300,6 @@
} }
} }
}, },
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"license": "MIT"
},
"node_modules/decimal.js-light": { "node_modules/decimal.js-light": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
@ -8687,21 +8143,6 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/icu-minify": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.2.tgz",
"integrity": "sha512-LHBQV+skKkjZSPd590pZ7ZAHftUgda3eFjeuNwA8/15L8T8loCNBktKQyTlkodAU86KovFXeg/9WntlAo5wA5A==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/icu-messageformat-parser": "^3.4.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -8800,18 +8241,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/intl-messageformat": {
"version": "11.1.2",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.1.2.tgz",
"integrity": "sha512-ucSrQmZGAxfiBHfBRXW/k7UC8MaGFlEj4Ry1tKiDcmgwQm1y3EDl40u+4VNHYomxJQMJi9NEI3riDRlth96jKg==",
"license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "3.1.1",
"@formatjs/fast-memoize": "3.1.0",
"@formatjs/icu-messageformat-parser": "3.5.1",
"tslib": "^2.8.1"
}
},
"node_modules/iobuffer": { "node_modules/iobuffer": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
@ -8991,6 +8420,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -9035,6 +8465,7 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
@ -10848,15 +10279,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/next": { "node_modules/next": {
"version": "15.5.10", "version": "15.5.10",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.10.tgz", "resolved": "https://registry.npmjs.org/next/-/next-15.5.10.tgz",
@ -10965,93 +10387,6 @@
} }
} }
}, },
"node_modules/next-intl": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.8.2.tgz",
"integrity": "sha512-GuuwyvyEI49/oehQbBXEoY8KSIYCzmfMLhmIwhMXTb+yeBmly1PnJcpgph3KczQ+HTJMXwXCmkizgtT8jBMf3A==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"@parcel/watcher": "^2.4.1",
"@swc/core": "^1.15.2",
"icu-minify": "^4.8.2",
"negotiator": "^1.0.0",
"next-intl-swc-plugin-extractor": "^4.8.2",
"po-parser": "^2.1.1",
"use-intl": "^4.8.2"
},
"peerDependencies": {
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
"typescript": "^5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/next-intl-swc-plugin-extractor": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.8.2.tgz",
"integrity": "sha512-sHDs36L1VZmFHj3tPHsD+KZJtnsRudHlNvT0ieIe3iFVn5OpGLTxW3d/Zc/2LXSj5GpGuR6wQeikbhFjU9tMQQ==",
"license": "MIT"
},
"node_modules/next-intl/node_modules/@swc/core": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz",
"integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3",
"@swc/types": "^0.1.25"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.15.11",
"@swc/core-darwin-x64": "1.15.11",
"@swc/core-linux-arm-gnueabihf": "1.15.11",
"@swc/core-linux-arm64-gnu": "1.15.11",
"@swc/core-linux-arm64-musl": "1.15.11",
"@swc/core-linux-x64-gnu": "1.15.11",
"@swc/core-linux-x64-musl": "1.15.11",
"@swc/core-win32-arm64-msvc": "1.15.11",
"@swc/core-win32-ia32-msvc": "1.15.11",
"@swc/core-win32-x64-msvc": "1.15.11"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
},
"peerDependenciesMeta": {
"@swc/helpers": {
"optional": true
}
}
},
"node_modules/next-intl/node_modules/@swc/helpers": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz",
"integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==",
"license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/next-themes": { "node_modules/next-themes": {
"version": "0.4.6", "version": "0.4.6",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
@ -11090,12 +10425,6 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@ -11514,6 +10843,7 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -11566,12 +10896,6 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/po-parser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz",
"integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==",
"license": "MIT"
},
"node_modules/possible-typed-array-names": { "node_modules/possible-typed-array-names": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@ -13988,27 +13312,6 @@
"react": "*" "react": "*"
} }
}, },
"node_modules/use-intl": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.8.2.tgz",
"integrity": "sha512-3VNXZgDnPFqhIYosQ9W1Hc6K5q+ZelMfawNbexdwL/dY7BTHbceLUBX5Eeex9lgogxTp0pf1SjHuhYNAjr9H3g==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "^3.1.0",
"@schummar/icu-type-parser": "1.21.5",
"icu-minify": "^4.8.2",
"intl-messageformat": "^11.1.0"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/use-isomorphic-layout-effect": { "node_modules/use-isomorphic-layout-effect": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",

View File

@ -70,7 +70,6 @@
"motion": "^11.15.0", "motion": "^11.15.0",
"next": "^15.1.0", "next": "^15.1.0",
"next-auth": "^5.0.0-beta.25", "next-auth": "^5.0.0-beta.25",
"next-intl": "^4.8.2",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"nodemailer": "^7.0.7", "nodemailer": "^7.0.7",
"openai": "^6.16.0", "openai": "^6.16.0",

View File

@ -1,6 +1,4 @@
import type { Metadata } from 'next' import type { Metadata } from 'next'
import { NextIntlClientProvider } from 'next-intl'
import { getLocale, getMessages } from 'next-intl/server'
import './globals.css' import './globals.css'
import { Providers } from './providers' import { Providers } from './providers'
import { Toaster } from 'sonner' import { Toaster } from 'sonner'
@ -16,20 +14,15 @@ export const metadata: Metadata = {
}, },
} }
export default async function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode children: React.ReactNode
}>) { }>) {
const locale = await getLocale()
const messages = await getMessages()
return ( return (
<html lang={locale} suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body className="min-h-screen bg-background font-sans antialiased"> <body className="min-h-screen bg-background font-sans antialiased">
<NextIntlClientProvider messages={messages}> <Providers>{children}</Providers>
<Providers>{children}</Providers>
</NextIntlClientProvider>
<Toaster <Toaster
position="top-right" position="top-right"
toastOptions={{ toastOptions={{

View File

@ -5,7 +5,6 @@ import Link from 'next/link'
import type { Route } from 'next' import type { Route } from 'next'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { signOut } from 'next-auth/react' import { signOut } from 'next-auth/react'
import { useTranslations } from 'next-intl'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { import {
@ -42,7 +41,6 @@ import { EditionSelector } from '@/components/shared/edition-selector'
import { useEdition } from '@/contexts/edition-context' import { useEdition } from '@/contexts/edition-context'
import { UserAvatar } from '@/components/shared/user-avatar' import { UserAvatar } from '@/components/shared/user-avatar'
import { NotificationBell } from '@/components/shared/notification-bell' import { NotificationBell } from '@/components/shared/notification-bell'
import { LanguageSwitcher } from '@/components/shared/language-switcher'
import { useSession } from 'next-auth/react' import { useSession } from 'next-auth/react'
import { trpc } from '@/lib/trpc/client' import { trpc } from '@/lib/trpc/client'
@ -147,7 +145,6 @@ const roleLabels: Record<string, string> = {
export function AdminSidebar({ user }: AdminSidebarProps) { export function AdminSidebar({ user }: AdminSidebarProps) {
const pathname = usePathname() const pathname = usePathname()
const tAuth = useTranslations('auth')
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const { status: sessionStatus } = useSession() const { status: sessionStatus } = useSession()
const isAuthenticated = sessionStatus === 'authenticated' const isAuthenticated = sessionStatus === 'authenticated'
@ -173,7 +170,6 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
<div className="fixed top-0 left-0 right-0 z-40 flex h-16 items-center justify-between border-b bg-card px-4 lg:hidden"> <div className="fixed top-0 left-0 right-0 z-40 flex h-16 items-center justify-between border-b bg-card px-4 lg:hidden">
<Logo showText textSuffix="Admin" /> <Logo showText textSuffix="Admin" />
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<LanguageSwitcher />
<NotificationBell /> <NotificationBell />
<Button <Button
variant="ghost" variant="ghost"
@ -209,7 +205,6 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
<div className="flex h-16 items-center justify-between border-b px-6"> <div className="flex h-16 items-center justify-between border-b px-6">
<Logo showText textSuffix="Admin" /> <Logo showText textSuffix="Admin" />
<div className="hidden lg:flex items-center gap-1"> <div className="hidden lg:flex items-center gap-1">
<LanguageSwitcher />
<NotificationBell /> <NotificationBell />
</div> </div>
</div> </div>
@ -349,7 +344,7 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-destructive focus:bg-destructive/10 focus:text-destructive" className="flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-destructive focus:bg-destructive/10 focus:text-destructive"
> >
<LogOut className="h-4 w-4" /> <LogOut className="h-4 w-4" />
<span>{tAuth('signOut')}</span> <span>Sign Out</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@ -2,32 +2,30 @@
import { Home, Users, FileText, MessageSquare } from 'lucide-react' import { Home, Users, FileText, MessageSquare } from 'lucide-react'
import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav' import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav'
import { useTranslations } from 'next-intl'
interface ApplicantNavProps { interface ApplicantNavProps {
user: RoleNavUser user: RoleNavUser
} }
export function ApplicantNav({ user }: ApplicantNavProps) { export function ApplicantNav({ user }: ApplicantNavProps) {
const t = useTranslations('nav')
const navigation: NavItem[] = [ const navigation: NavItem[] = [
{ {
name: t('dashboard'), name: 'Dashboard',
href: '/applicant', href: '/applicant',
icon: Home, icon: Home,
}, },
{ {
name: t('team'), name: 'Team',
href: '/applicant/team', href: '/applicant/team',
icon: Users, icon: Users,
}, },
{ {
name: t('documents'), name: 'Documents',
href: '/applicant/documents', href: '/applicant/documents',
icon: FileText, icon: FileText,
}, },
{ {
name: t('mentoring'), name: 'Mentoring',
href: '/applicant/mentor', href: '/applicant/mentor',
icon: MessageSquare, icon: MessageSquare,
}, },

View File

@ -4,7 +4,6 @@ import { BookOpen, ClipboardList, GitCompare, Home, Trophy } from 'lucide-react'
import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav' import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav'
import { trpc } from '@/lib/trpc/client' import { trpc } from '@/lib/trpc/client'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { useTranslations } from 'next-intl'
interface JuryNavProps { interface JuryNavProps {
user: RoleNavUser user: RoleNavUser
@ -43,30 +42,29 @@ function RemainingBadge() {
} }
export function JuryNav({ user }: JuryNavProps) { export function JuryNav({ user }: JuryNavProps) {
const t = useTranslations('nav')
const navigation: NavItem[] = [ const navigation: NavItem[] = [
{ {
name: t('dashboard'), name: 'Dashboard',
href: '/jury', href: '/jury',
icon: Home, icon: Home,
}, },
{ {
name: t('assignments'), name: 'Assignments',
href: '/jury/assignments', href: '/jury/assignments',
icon: ClipboardList, icon: ClipboardList,
}, },
{ {
name: t('awards'), name: 'Awards',
href: '/jury/awards', href: '/jury/awards',
icon: Trophy, icon: Trophy,
}, },
{ {
name: t('compare'), name: 'Compare',
href: '/jury/compare', href: '/jury/compare',
icon: GitCompare, icon: GitCompare,
}, },
{ {
name: t('learningHub'), name: 'Learning Hub',
href: '/jury/learning', href: '/jury/learning',
icon: BookOpen, icon: BookOpen,
}, },

View File

@ -2,27 +2,25 @@
import { BookOpen, Home, Users } from 'lucide-react' import { BookOpen, Home, Users } from 'lucide-react'
import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav' import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav'
import { useTranslations } from 'next-intl'
interface MentorNavProps { interface MentorNavProps {
user: RoleNavUser user: RoleNavUser
} }
export function MentorNav({ user }: MentorNavProps) { export function MentorNav({ user }: MentorNavProps) {
const t = useTranslations('nav')
const navigation: NavItem[] = [ const navigation: NavItem[] = [
{ {
name: t('dashboard'), name: 'Dashboard',
href: '/mentor', href: '/mentor',
icon: Home, icon: Home,
}, },
{ {
name: t('myProjects'), name: 'My Projects',
href: '/mentor/projects', href: '/mentor/projects',
icon: Users, icon: Users,
}, },
{ {
name: t('learningHub'), name: 'Learning Hub',
href: '/mentor/resources', href: '/mentor/resources',
icon: BookOpen, icon: BookOpen,
}, },

View File

@ -2,22 +2,20 @@
import { BarChart3, Home } from 'lucide-react' import { BarChart3, Home } from 'lucide-react'
import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav' import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav'
import { useTranslations } from 'next-intl'
interface ObserverNavProps { interface ObserverNavProps {
user: RoleNavUser user: RoleNavUser
} }
export function ObserverNav({ user }: ObserverNavProps) { export function ObserverNav({ user }: ObserverNavProps) {
const t = useTranslations('nav')
const navigation: NavItem[] = [ const navigation: NavItem[] = [
{ {
name: t('dashboard'), name: 'Dashboard',
href: '/observer', href: '/observer',
icon: Home, icon: Home,
}, },
{ {
name: t('reports'), name: 'Reports',
href: '/observer/reports', href: '/observer/reports',
icon: BarChart3, icon: BarChart3,
}, },

View File

@ -4,7 +4,6 @@ import { useState, useEffect } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { signOut, useSession } from 'next-auth/react' import { signOut, useSession } from 'next-auth/react'
import { useTranslations } from 'next-intl'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { UserAvatar } from '@/components/shared/user-avatar' import { UserAvatar } from '@/components/shared/user-avatar'
@ -22,7 +21,6 @@ import { LogOut, Menu, Moon, Settings, Sun, User, X } from 'lucide-react'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
import { Logo } from '@/components/shared/logo' import { Logo } from '@/components/shared/logo'
import { NotificationBell } from '@/components/shared/notification-bell' import { NotificationBell } from '@/components/shared/notification-bell'
import { LanguageSwitcher } from '@/components/shared/language-switcher'
export type NavItem = { export type NavItem = {
name: string name: string
@ -51,8 +49,6 @@ function isNavItemActive(pathname: string, href: string, basePath: string): bool
export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: RoleNavProps) { export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: RoleNavProps) {
const pathname = usePathname() const pathname = usePathname()
const tCommon = useTranslations('common')
const tAuth = useTranslations('auth')
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const { status: sessionStatus } = useSession() const { status: sessionStatus } = useSession()
const isAuthenticated = sessionStatus === 'authenticated' const isAuthenticated = sessionStatus === 'authenticated'
@ -111,7 +107,6 @@ export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: R
)} )}
</Button> </Button>
)} )}
<LanguageSwitcher />
<NotificationBell /> <NotificationBell />
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -135,7 +130,7 @@ export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: R
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href={"/settings/profile" as Route} className="flex cursor-pointer items-center"> <Link href={"/settings/profile" as Route} className="flex cursor-pointer items-center">
<Settings className="mr-2 h-4 w-4" /> <Settings className="mr-2 h-4 w-4" />
{tCommon('settings')} Settings
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
@ -144,7 +139,7 @@ export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: R
className="text-destructive focus:text-destructive" className="text-destructive focus:text-destructive"
> >
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 h-4 w-4" />
{tAuth('signOut')} Sign Out
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@ -196,7 +191,7 @@ export function RoleNav({ navigation, roleName, user, basePath, statusBadge }: R
onClick={() => signOut({ callbackUrl: '/login' })} onClick={() => signOut({ callbackUrl: '/login' })}
> >
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 h-4 w-4" />
{tAuth('signOut')} Sign Out
</Button> </Button>
</div> </div>
</nav> </nav>

View File

@ -1,61 +0,0 @@
'use client'
import { useTransition } from 'react'
import { useLocale } from 'next-intl'
import { useRouter } from 'next/navigation'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Globe, Check } from 'lucide-react'
const LANGUAGES = [
{ code: 'en', label: 'English', flag: 'EN' },
{ code: 'fr', label: 'Fran\u00e7ais', flag: 'FR' },
] as const
type LanguageCode = (typeof LANGUAGES)[number]['code']
export function LanguageSwitcher() {
const locale = useLocale() as LanguageCode
const router = useRouter()
const [isPending, startTransition] = useTransition()
const currentLang = LANGUAGES.find((l) => l.code === locale) ?? LANGUAGES[0]
const switchLanguage = (code: LanguageCode) => {
// Set cookie with 1 year expiry
document.cookie = `locale=${code};path=/;max-age=${365 * 24 * 60 * 60};samesite=lax`
// Refresh to re-run server components with new locale
startTransition(() => {
router.refresh()
})
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="gap-2" disabled={isPending}>
<Globe className="h-4 w-4" />
<span className="font-medium">{currentLang.flag}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{LANGUAGES.map((lang) => (
<DropdownMenuItem
key={lang.code}
onClick={() => switchLanguage(lang.code)}
className="gap-2"
>
<span className="font-medium w-6">{lang.flag}</span>
<span>{lang.label}</span>
{locale === lang.code && <Check className="ml-auto h-4 w-4" />}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@ -1,22 +0,0 @@
import { getRequestConfig } from 'next-intl/server'
import { cookies } from 'next/headers'
export const supportedLocales = ['en', 'fr'] as const
export type SupportedLocale = (typeof supportedLocales)[number]
export const defaultLocale: SupportedLocale = 'en'
export default getRequestConfig(async () => {
const store = await cookies()
const cookieLocale = store.get('locale')?.value
// Validate the locale from cookie
const locale = supportedLocales.includes(cookieLocale as SupportedLocale)
? (cookieLocale as SupportedLocale)
: defaultLocale
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
}
})