mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-20 21:59:04 +02:00
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:
7 files changed
+580
-10
No files matched your search
@@ -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();
|
||||
@@ -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();
|
||||
Reference in new issue
Block a user