Feature: Système de tags et liste automatique des modèles OpenRouter

Ajouts majeurs:
- Système de tags complet pour les notes
  * Table note_tags en base de données avec indexes
  * API REST pour CRUD des tags (GET/POST/DELETE)
  * UI avec onglet Tags dans l'éditeur de notes
  * Badges cliquables avec icônes et suppression au survol
  * Modal pour ajouter des tags

- Liste automatique des modèles OpenRouter
  * Nouvelle route API /api/openrouter/models
  * Cache des modèles avec rafraîchissement automatique (1h)
  * Dropdown dans l'admin avec tous les modèles disponibles
  * Bouton "Actualiser" pour forcer le refresh
  * Affichage du nom et description de chaque modèle
  * Fallback sur input manuel si pas de clé API

Améliorations UI:
- Ajout de dates de création et modification dans les notes
- Affichage des dates dans les cartes et l'éditeur
- Format français pour toutes les dates
- Onglets passés de 4 à 5 colonnes pour inclure Tags
- Animation de chargement sur le bouton refresh des modèles

Architecture:
- Nouveau service TagsService.ts pour la gestion des tags
- Nouveau service OpenRouterService.ts pour l'API OpenRouter
- Nouvelles routes openrouter.routes.js
- Support Select component pour les dropdowns
- Modals InputModal utilisés pour les tags
This commit is contained in:
Claude committed 2025-11-12 09:47:47 +00:00
1 parent ece7232f0d
commit 4425695b4f
7 files changed
+580 -10

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
import AuthService from './AuthService';
export interface OpenRouterModel {
id: string;
name: string;
description: string;
context_length: number;
pricing: {
prompt?: string;
completion?: string;
};
}
class OpenRouterService {
/**
* Get available models from OpenRouter API
*/
async getModels(): Promise<OpenRouterModel[]> {
try {
const response = await fetch('/api/openrouter/models', {
headers: AuthService.getHeaders()
});
if (!response.ok) {
throw new Error('Erreur lors de la récupération des modèles');
}
const models = await response.json();
return Array.isArray(models) ? models : [];
} catch (error) {
console.error('Erreur lors de la récupération des modèles OpenRouter:', error);
return [];
}
}
/**
* Refresh models cache
*/
async refreshModels(): Promise<OpenRouterModel[]> {
try {
const response = await fetch('/api/openrouter/refresh-models', {
method: 'POST',
headers: AuthService.getHeaders()
});
if (!response.ok) {
throw new Error('Erreur lors du rafraîchissement des modèles');
}
const data = await response.json();
return Array.isArray(data.models) ? data.models : [];
} catch (error) {
console.error('Erreur lors du rafraîchissement des modèles:', error);
throw error;
}
}
}
export default new OpenRouterService();
+77
View File
@@ -0,0 +1,77 @@
import AuthService from './AuthService';
export interface Tag {
id: number;
tag: string;
}
class TagsService {
/**
* Get all tags for a note
*/
async getTags(noteId: number): Promise<Tag[]> {
try {
const response = await fetch(`/api/notes/${noteId}/tags`, {
headers: AuthService.getHeaders()
});
if (!response.ok) {
throw new Error('Erreur lors de la récupération des tags');
}
return await response.json();
} catch (error) {
console.error('Erreur lors de la récupération des tags:', error);
throw error;
}
}
/**
* Add a tag to a note
*/
async addTag(noteId: number, tag: string): Promise<Tag> {
try {
const response = await fetch(`/api/notes/${noteId}/tags`, {
method: 'POST',
headers: {
...AuthService.getHeaders(),
'Content-Type': 'application/json'
},
body: JSON.stringify({ tag })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Erreur lors de l\'ajout du tag');
}
return await response.json();
} catch (error) {
console.error('Erreur lors de l\'ajout du tag:', error);
throw error;
}
}
/**
* Delete a tag from a note
*/
async deleteTag(noteId: number, tagId: number): Promise<boolean> {
try {
const response = await fetch(`/api/notes/${noteId}/tags/${tagId}`, {
method: 'DELETE',
headers: AuthService.getHeaders()
});
if (!response.ok) {
throw new Error('Erreur lors de la suppression du tag');
}
return true;
} catch (error) {
console.error('Erreur lors de la suppression du tag:', error);
throw error;
}
}
}
export default new TagsService();