Ver código fonte

Refactor pinned articles display

gpt-engineer-app[bot] 2 meses atrás
pai
commit
62f3cd19dc
2 arquivos alterados com 144 adições e 20 exclusões
  1. 108 11
      src/components/CategoryFilter.tsx
  2. 36 9
      src/pages/Index.tsx

+ 108 - 11
src/components/CategoryFilter.tsx

@@ -13,9 +13,17 @@ import {
   Calendar,
   Clock,
   Heart,
-  Eye
+  Eye,
+  ChevronDown,
+  ChevronRight,
+  BookOpen,
+  Trash2
 } from 'lucide-react';
 import { useAuth } from '@/hooks/useAuth';
+import { NewsItem } from '@/types/news';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
+import { ScrollArea } from '@/components/ui/scroll-area';
+import { useState } from 'react';
 
 interface CategoryFilterProps {
   categories: NewsCategory[];
@@ -24,12 +32,16 @@ interface CategoryFilterProps {
   newsCount: number;
   pinnedCount?: number;
   articles: any[]; // Add articles to calculate counts per category
+  pinnedArticles?: NewsItem[]; // Pinned articles to display
   dateFilter?: 'today' | 'yesterday' | null;
   onDateFilterChange?: (filter: 'today' | 'yesterday' | null) => void;
   showFollowedOnly?: boolean;
   onShowFollowedOnlyChange?: (showFollowedOnly: boolean) => void;
   showReadArticles?: boolean;
   onShowReadArticlesChange?: (showReadArticles: boolean) => void;
+  onTogglePin?: (articleId: string) => void;
+  onMarkAsRead?: (articleId: string) => void;
+  onDeleteArticle?: (articleId: string) => void;
 }
 
 const iconMap = {
@@ -46,14 +58,19 @@ const CategoryFilter = ({
   newsCount,
   pinnedCount = 0,
   articles,
+  pinnedArticles = [],
   dateFilter,
   onDateFilterChange,
   showFollowedOnly,
   onShowFollowedOnlyChange,
   showReadArticles,
-  onShowReadArticlesChange
+  onShowReadArticlesChange,
+  onTogglePin,
+  onMarkAsRead,
+  onDeleteArticle
 }: CategoryFilterProps) => {
   const { user } = useAuth();
+  const [isPinnedExpanded, setIsPinnedExpanded] = useState(false);
 
   // Calculate count for each category
   const getCategoryCount = (categoryType: string) => {
@@ -202,16 +219,96 @@ const CategoryFilter = ({
               </div>
             )}
 
-            {/* Section 3: Compteur d'épinglés */}
+            {/* Section 3: Articles épinglés */}
             {user && (
-              <div className="flex flex-col gap-2 min-w-fit">
-                <div className="flex items-center gap-2 mb-1">
-                  <Pin className="h-4 w-4 text-muted-foreground" />
-                  <span className="text-sm font-medium text-muted-foreground">Épinglés</span>
-                </div>
-                <Badge variant="secondary" className="w-fit">
-                  {pinnedCount > 0 ? `${pinnedCount}` : 'Aucun'}
-                </Badge>
+              <div className="col-span-full">
+                <Collapsible open={isPinnedExpanded} onOpenChange={setIsPinnedExpanded}>
+                  <CollapsibleTrigger asChild>
+                    <Button variant="outline" className="w-full justify-between p-3">
+                      <div className="flex items-center gap-2">
+                        <Pin className="h-4 w-4 text-muted-foreground" />
+                        <span className="text-sm font-medium">Articles épinglés</span>
+                        <Badge variant="secondary">{pinnedCount}</Badge>
+                      </div>
+                      {isPinnedExpanded ? (
+                        <ChevronDown className="h-4 w-4" />
+                      ) : (
+                        <ChevronRight className="h-4 w-4" />
+                      )}
+                    </Button>
+                  </CollapsibleTrigger>
+                  <CollapsibleContent className="space-y-2 mt-2">
+                    {pinnedArticles.length === 0 ? (
+                      <div className="text-center py-4 text-muted-foreground text-sm">
+                        Aucun article épinglé
+                      </div>
+                    ) : (
+                      <ScrollArea className="max-h-96">
+                        <div className="space-y-2 pr-2">
+                          {pinnedArticles.slice(0, 10).map((article) => (
+                            <div
+                              key={article.id}
+                              className="border rounded-lg p-3 bg-card hover:bg-muted/50 transition-colors"
+                            >
+                              <div className="flex items-start gap-2">
+                                <div className="flex-1 min-w-0">
+                                  <h4 className="text-sm font-medium line-clamp-2 mb-1">
+                                    {article.title}
+                                  </h4>
+                                  <div className="flex items-center gap-2 text-xs text-muted-foreground">
+                                    <span>{article.source}</span>
+                                    <span>•</span>
+                                    <span>{new Date(article.publishedAt).toLocaleDateString('fr-FR')}</span>
+                                  </div>
+                                </div>
+                                <div className="flex items-center gap-1">
+                                  {!article.isRead && onMarkAsRead && (
+                                    <Button
+                                      variant="ghost"
+                                      size="sm"
+                                      className="h-7 w-7 p-0"
+                                      onClick={() => onMarkAsRead(article.id)}
+                                      title="Marquer comme lu"
+                                    >
+                                      <BookOpen className="h-3 w-3" />
+                                    </Button>
+                                  )}
+                                  {onTogglePin && (
+                                    <Button
+                                      variant="ghost"
+                                      size="sm"
+                                      className="h-7 w-7 p-0"
+                                      onClick={() => onTogglePin(article.id)}
+                                      title="Désépingler"
+                                    >
+                                      <Pin className="h-3 w-3 fill-current" />
+                                    </Button>
+                                  )}
+                                  {onDeleteArticle && (
+                                    <Button
+                                      variant="ghost"
+                                      size="sm"
+                                      className="h-7 w-7 p-0 text-destructive hover:text-destructive"
+                                      onClick={() => onDeleteArticle(article.id)}
+                                      title="Supprimer"
+                                    >
+                                      <Trash2 className="h-3 w-3" />
+                                    </Button>
+                                  )}
+                                </div>
+                              </div>
+                            </div>
+                          ))}
+                          {pinnedArticles.length > 10 && (
+                            <div className="text-center py-2 text-xs text-muted-foreground">
+                              +{pinnedArticles.length - 10} articles supplémentaires
+                            </div>
+                          )}
+                        </div>
+                      </ScrollArea>
+                    )}
+                  </CollapsibleContent>
+                </Collapsible>
               </div>
             )}
             

+ 36 - 9
src/pages/Index.tsx

@@ -65,6 +65,15 @@ const Index = () => {
       return new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime();
     });
   }, [articles, selectedCategory, searchQuery]);
+
+  // Separate pinned and regular articles
+  const pinnedArticles = useMemo(() => {
+    return filteredNews.filter(article => article.isPinned);
+  }, [filteredNews]);
+
+  const regularArticles = useMemo(() => {
+    return filteredNews.filter(article => !article.isPinned);
+  }, [filteredNews]);
   const pinnedCount = articles.filter(item => item.isPinned).length;
   const unreadCount = articles.filter(item => !item.isRead).length;
 
@@ -140,12 +149,16 @@ const Index = () => {
                 newsCount={articles.length} 
                 pinnedCount={pinnedCount} 
                 articles={articles}
-                          dateFilter={dateFilter}
-                          onDateFilterChange={setDateFilter}
-                          showFollowedOnly={showFollowedOnly}
-                          onShowFollowedOnlyChange={handleShowFollowedOnlyChange}
-                          showReadArticles={showReadArticles}
-                          onShowReadArticlesChange={setShowReadArticles}
+                pinnedArticles={pinnedArticles}
+                dateFilter={dateFilter}
+                onDateFilterChange={setDateFilter}
+                showFollowedOnly={showFollowedOnly}
+                onShowFollowedOnlyChange={handleShowFollowedOnlyChange}
+                showReadArticles={showReadArticles}
+                onShowReadArticlesChange={setShowReadArticles}
+                onTogglePin={togglePin}
+                onMarkAsRead={markAsRead}
+                onDeleteArticle={deleteArticle}
               />
               
               <div className="bg-card border rounded-lg p-4 space-y-3">
@@ -197,12 +210,16 @@ const Index = () => {
                           newsCount={articles.length} 
                           pinnedCount={pinnedCount} 
                           articles={articles}
+                          pinnedArticles={pinnedArticles}
                           dateFilter={dateFilter}
                           onDateFilterChange={setDateFilter}
                           showFollowedOnly={showFollowedOnly}
                           onShowFollowedOnlyChange={handleShowFollowedOnlyChange}
                           showReadArticles={showReadArticles}
                           onShowReadArticlesChange={setShowReadArticles}
+                          onTogglePin={togglePin}
+                          onMarkAsRead={markAsRead}
+                          onDeleteArticle={deleteArticle}
                         />
                         
                         <div className="bg-card border rounded-lg p-4 space-y-3">
@@ -242,17 +259,27 @@ const Index = () => {
               </div>
             </div>
             
-            {filteredNews.length === 0 && articles.length > 0 ? <div className="text-center py-12">
+            {regularArticles.length === 0 && articles.length > 0 ? <div className="text-center py-12">
                 <p className="text-muted-foreground text-lg">Aucun article trouvé avec ces filtres</p>
                 <p className="text-sm text-muted-foreground mt-2">
                   Essayez de modifier vos filtres ou votre recherche
                 </p>
-              </div> : filteredNews.length === 0 ? <div className="text-center py-12">
+                {pinnedArticles.length > 0 && (
+                  <p className="text-xs text-muted-foreground mt-2">
+                    ({pinnedArticles.length} article{pinnedArticles.length > 1 ? 's' : ''} épinglé{pinnedArticles.length > 1 ? 's' : ''} dans la sidebar)
+                  </p>
+                )}
+              </div> : regularArticles.length === 0 ? <div className="text-center py-12">
                 <Rss className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
                 <p className="text-muted-foreground text-lg">Aucun article non lu disponible</p>
                 <p className="text-sm text-muted-foreground mt-2 mb-4">
                   {user ? 'Bravo ! Tous vos articles sont lus ou suivez des flux RSS pour voir des articles ici' : 'Aucun article public disponible pour le moment'}
                 </p>
+                {pinnedArticles.length > 0 && (
+                  <p className="text-xs text-muted-foreground mb-4">
+                    ({pinnedArticles.length} article{pinnedArticles.length > 1 ? 's' : ''} épinglé{pinnedArticles.length > 1 ? 's' : ''} dans la sidebar)
+                  </p>
+                )}
                 {user && <div className="flex gap-2 justify-center">
                     <Link to="/feeds">
                       <Button variant="outline">
@@ -265,7 +292,7 @@ const Index = () => {
                     </Button>
                   </div>}
               </div> : <div className="space-y-4">
-                {filteredNews.map(item => <NewsCard key={item.id} news={item} onTogglePin={togglePin} onMarkAsRead={markAsRead} onDelete={deleteArticle} onOpenArticle={handleOpenArticle} />)}
+                {regularArticles.map(item => <NewsCard key={item.id} news={item} onTogglePin={togglePin} onMarkAsRead={markAsRead} onDelete={deleteArticle} onOpenArticle={handleOpenArticle} />)}
               </div>}
           </div>
         </div>