mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-24 15:48:16 +02:00
Merge pull request #29 from R0m1k3/claude/fix-npm-ci-build-error-011CV3c2K4ETHdBRQUs7tkJf
Feature: Amélioration de l'interface Todos et RSS avec onglets
This commit is contained in:
2 files changed
+159
-95
No files matched your search
@@ -114,6 +114,30 @@ router.put('/:id',
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH /api/todos/:id/toggle
|
||||||
|
* Basculer l'état completed d'un todo
|
||||||
|
*/
|
||||||
|
router.patch('/:id/toggle', async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Vérifier que le todo appartient à l'utilisateur
|
||||||
|
const todo = await getOne('SELECT id, completed FROM global_todos WHERE id = ? AND user_id = ?', [req.params.id, req.user.id]);
|
||||||
|
if (!todo) {
|
||||||
|
return res.status(404).json({ error: 'Todo non trouvé' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCompleted = todo.completed ? 0 : 1;
|
||||||
|
await runQuery('UPDATE global_todos SET completed = ? WHERE id = ?', [newCompleted, req.params.id]);
|
||||||
|
|
||||||
|
logger.info(`Todo global ${newCompleted ? 'complété' : 'réouvert'} (ID: ${req.params.id}) par ${req.user.username}`);
|
||||||
|
|
||||||
|
res.json({ message: 'Todo modifié avec succès', completed: newCompleted === 1 });
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Erreur lors du toggle du todo:', error);
|
||||||
|
res.status(500).json({ error: 'Erreur serveur' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE /api/todos/:id
|
* DELETE /api/todos/:id
|
||||||
* Supprimer un todo global
|
* Supprimer un todo global
|
||||||
|
|||||||
+135
-95
@@ -755,105 +755,145 @@ const Index = () => {
|
|||||||
|
|
||||||
{/* Right Sidebar - Todos & RSS */}
|
{/* Right Sidebar - Todos & RSS */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Todos Box */}
|
{/* Boxes côte à côte */}
|
||||||
<Card>
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<CardHeader className="pb-3">
|
{/* Todos Box */}
|
||||||
<div className="flex items-center justify-between">
|
<Card>
|
||||||
<CardTitle className="text-lg flex items-center gap-2">
|
<CardHeader className="pb-3">
|
||||||
<CheckSquare className="h-5 w-5" />
|
<div className="flex items-center justify-between">
|
||||||
Tâches
|
<CardTitle className="text-base flex items-center gap-2">
|
||||||
</CardTitle>
|
<CheckSquare className="h-4 w-4" />
|
||||||
<Button size="sm" variant="ghost" onClick={handleAddTodo}>
|
Tâches
|
||||||
<Plus className="h-4 w-4" />
|
</CardTitle>
|
||||||
</Button>
|
<Button size="sm" variant="ghost" onClick={handleAddTodo} className="h-7 w-7 p-0">
|
||||||
</div>
|
<Plus className="h-3 w-3" />
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-2 max-h-[300px] overflow-y-auto">
|
|
||||||
{todos.length > 0 ? (
|
|
||||||
todos.map(todo => (
|
|
||||||
<div key={todo.id} className="flex items-center gap-2 p-2 border rounded-md hover:bg-accent/50 transition-colors group">
|
|
||||||
<Checkbox
|
|
||||||
checked={todo.completed}
|
|
||||||
onCheckedChange={() => todo.id && handleToggleTodo(todo.id)}
|
|
||||||
/>
|
|
||||||
<span className={`text-sm flex-1 ${todo.completed ? 'line-through text-muted-foreground' : ''}`}>
|
|
||||||
{todo.text}
|
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
||||||
onClick={() => todo.id && handleDeleteTodo(todo.id)}
|
|
||||||
>
|
|
||||||
<Trash2 className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<p className="text-sm text-center text-muted-foreground py-4">Aucune tâche</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* RSS Box */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader className="pb-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<CardTitle className="text-lg flex items-center gap-2">
|
|
||||||
<Rss className="h-5 w-5" />
|
|
||||||
Flux RSS
|
|
||||||
</CardTitle>
|
|
||||||
<div className="flex gap-1">
|
|
||||||
<Button size="sm" variant="ghost" onClick={handleRefreshRss}>
|
|
||||||
<RefreshCw className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="ghost" onClick={handleAddRssFeed}>
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CardHeader>
|
||||||
</CardHeader>
|
<CardContent className="pb-3">
|
||||||
<CardContent>
|
<Tabs defaultValue="active" className="w-full">
|
||||||
<div className="space-y-3 max-h-[400px] overflow-y-auto">
|
<TabsList className="grid grid-cols-2 w-full h-8">
|
||||||
{rssArticles.length > 0 ? (
|
<TabsTrigger value="active" className="text-xs">Actives</TabsTrigger>
|
||||||
rssArticles.map(article => (
|
<TabsTrigger value="completed" className="text-xs">Complétées</TabsTrigger>
|
||||||
<div
|
</TabsList>
|
||||||
key={article.id}
|
|
||||||
className="p-2 border rounded-md hover:bg-accent/50 transition-colors cursor-pointer"
|
<TabsContent value="active" className="mt-2">
|
||||||
onClick={() => article.link && window.open(article.link, '_blank')}
|
<div className="space-y-2 max-h-[250px] overflow-y-auto">
|
||||||
>
|
{todos.filter(t => !t.completed).length > 0 ? (
|
||||||
<div className="flex items-start justify-between gap-2">
|
todos.filter(t => !t.completed).map(todo => (
|
||||||
<div className="flex-1 min-w-0">
|
<div key={todo.id} className="flex items-center gap-2 p-2 border rounded-md hover:bg-accent/50 transition-colors group">
|
||||||
<h4 className="text-sm font-medium line-clamp-2 mb-1">{article.title}</h4>
|
<Checkbox
|
||||||
{article.description && (
|
checked={false}
|
||||||
<p className="text-xs text-muted-foreground line-clamp-2">
|
onCheckedChange={() => todo.id && handleToggleTodo(todo.id)}
|
||||||
{article.description.replace(/<[^>]*>/g, '')}
|
/>
|
||||||
</p>
|
<span className="text-xs flex-1">
|
||||||
)}
|
{todo.text}
|
||||||
</div>
|
</span>
|
||||||
<ExternalLink className="h-3 w-3 text-muted-foreground flex-shrink-0 mt-1" />
|
<Button
|
||||||
</div>
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-5 w-5 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||||
|
onClick={() => todo.id && handleDeleteTodo(todo.id)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-center text-muted-foreground py-4">Aucune tâche active</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))
|
</TabsContent>
|
||||||
) : (
|
|
||||||
<p className="text-sm text-center text-muted-foreground py-4">
|
<TabsContent value="completed" className="mt-2">
|
||||||
Aucun article
|
<div className="space-y-2 max-h-[250px] overflow-y-auto">
|
||||||
<br />
|
{todos.filter(t => t.completed).length > 0 ? (
|
||||||
<Button
|
todos.filter(t => t.completed).map(todo => (
|
||||||
variant="link"
|
<div key={todo.id} className="flex items-center gap-2 p-2 border rounded-md hover:bg-accent/50 transition-colors group">
|
||||||
size="sm"
|
<Checkbox
|
||||||
className="mt-2"
|
checked={true}
|
||||||
onClick={handleAddRssFeed}
|
onCheckedChange={() => todo.id && handleToggleTodo(todo.id)}
|
||||||
>
|
/>
|
||||||
Ajouter un flux RSS
|
<span className="text-xs flex-1 line-through text-muted-foreground">
|
||||||
|
{todo.text}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-5 w-5 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||||
|
onClick={() => todo.id && handleDeleteTodo(todo.id)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-center text-muted-foreground py-4">Aucune tâche complétée</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* RSS Box */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle className="text-base flex items-center gap-2">
|
||||||
|
<Rss className="h-4 w-4" />
|
||||||
|
Flux RSS
|
||||||
|
</CardTitle>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<Button size="sm" variant="ghost" onClick={handleRefreshRss} className="h-7 w-7 p-0">
|
||||||
|
<RefreshCw className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
<Button size="sm" variant="ghost" onClick={handleAddRssFeed} className="h-7 w-7 p-0">
|
||||||
)}
|
<Plus className="h-3 w-3" />
|
||||||
</div>
|
</Button>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="pb-3">
|
||||||
|
<div className="space-y-2 max-h-[330px] overflow-y-auto">
|
||||||
|
{rssArticles.length > 0 ? (
|
||||||
|
rssArticles.map(article => (
|
||||||
|
<div
|
||||||
|
key={article.id}
|
||||||
|
className="p-2 border rounded-md hover:bg-accent/50 transition-colors cursor-pointer"
|
||||||
|
onClick={() => article.link && window.open(article.link, '_blank')}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h4 className="text-xs font-medium line-clamp-2 mb-1">{article.title}</h4>
|
||||||
|
{article.description && (
|
||||||
|
<p className="text-xs text-muted-foreground line-clamp-2">
|
||||||
|
{article.description.replace(/<[^>]*>/g, '')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ExternalLink className="h-3 w-3 text-muted-foreground flex-shrink-0 mt-1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-center text-muted-foreground py-4">
|
||||||
|
Aucun article
|
||||||
|
<br />
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
className="mt-2 text-xs"
|
||||||
|
onClick={handleAddRssFeed}
|
||||||
|
>
|
||||||
|
Ajouter un flux RSS
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user