Browse Source

Implement pinned articles page

gpt-engineer-app[bot] 2 months ago
parent
commit
997ce74731
3 changed files with 117 additions and 1 deletions
  1. 2 0
      src/App.tsx
  2. 13 1
      src/components/Header.tsx
  3. 102 0
      src/pages/Pinned.tsx

+ 2 - 0
src/App.tsx

@@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
 import { BrowserRouter, Routes, Route } from "react-router-dom";
 import Index from "./pages/Index";
 import FeedsManagement from "./pages/FeedsManagement";
+import Pinned from "./pages/Pinned";
 import NotFound from "./pages/NotFound";
 import Auth from "./pages/Auth";
 
@@ -19,6 +20,7 @@ const App = () => (
         <Routes>
           <Route path="/" element={<Index />} />
           <Route path="/feeds" element={<FeedsManagement />} />
+          <Route path="/pinned" element={<Pinned />} />
           <Route path="/auth" element={<Auth />} />
           {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
           <Route path="*" element={<NotFound />} />

+ 13 - 1
src/components/Header.tsx

@@ -1,7 +1,7 @@
 import { Button } from '@/components/ui/button';
 import { Input } from '@/components/ui/input';
 import { Badge } from '@/components/ui/badge';
-import { Search, Plus, Settings, User, Rss, List, LogOut, Shield } from 'lucide-react';
+import { Search, Plus, Settings, User, Rss, List, LogOut, Shield, Pin } from 'lucide-react';
 import { Link } from 'react-router-dom';
 import { useAuth } from '@/hooks/useAuth';
 import { useSuperUser } from '@/hooks/useSuperUser';
@@ -51,6 +51,18 @@ const Header = ({
             </div>
             
             {user ? <>
+                <Link to="/pinned">
+                  <Button variant="outline" size="sm" className="gap-2">
+                    <Pin className="h-4 w-4" />
+                    Épinglés
+                    {pinnedCount > 0 && (
+                      <Badge variant="secondary" className="ml-1 h-5 px-1.5">
+                        {pinnedCount}
+                      </Badge>
+                    )}
+                  </Button>
+                </Link>
+                
                 <Link to="/feeds">
                   <Button variant="outline" size="sm" className="gap-2">
                     <List className="h-4 w-4" />

+ 102 - 0
src/pages/Pinned.tsx

@@ -0,0 +1,102 @@
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Pin, ArrowLeft } from "lucide-react";
+import { useRealArticles } from "@/hooks/useRealArticles";
+import { useAuth } from "@/hooks/useAuth";
+import NewsCard from "@/components/NewsCard";
+import ArticleModal from "@/components/ArticleModal";
+import { NewsItem } from "@/types/news";
+import { Button } from "@/components/ui/button";
+
+const Pinned = () => {
+  const navigate = useNavigate();
+  const { user } = useAuth();
+  const { articles, loading, togglePin, markAsRead, deleteArticle } = useRealArticles();
+  const [selectedArticle, setSelectedArticle] = useState<NewsItem | null>(null);
+
+  // Redirect if not authenticated
+  if (!user) {
+    navigate("/auth");
+    return null;
+  }
+
+  // Filter only pinned articles
+  const pinnedArticles = articles.filter(article => article.isPinned);
+
+  if (loading) {
+    return (
+      <div className="min-h-screen bg-background flex items-center justify-center">
+        <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="min-h-screen bg-background">
+      {/* Header */}
+      <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
+        <div className="container flex h-16 items-center justify-between px-4">
+          <div className="flex items-center gap-4">
+            <Button
+              variant="ghost"
+              size="icon"
+              onClick={() => navigate("/")}
+              aria-label="Retour à l'accueil"
+            >
+              <ArrowLeft className="h-5 w-5" />
+            </Button>
+            <div className="flex items-center gap-2">
+              <Pin className="h-6 w-6 text-primary" />
+              <h1 className="text-2xl font-bold">Articles épinglés</h1>
+              {pinnedArticles.length > 0 && (
+                <span className="ml-2 rounded-full bg-primary/10 px-3 py-1 text-sm font-medium text-primary">
+                  {pinnedArticles.length}
+                </span>
+              )}
+            </div>
+          </div>
+        </div>
+      </header>
+
+      {/* Content */}
+      <main className="container mx-auto px-4 py-8">
+        {pinnedArticles.length === 0 ? (
+          <div className="flex flex-col items-center justify-center py-16 text-center">
+            <Pin className="h-16 w-16 text-muted-foreground mb-4" />
+            <h2 className="text-2xl font-semibold mb-2">Aucun article épinglé</h2>
+            <p className="text-muted-foreground mb-6 max-w-md">
+              Vous n'avez pas encore épinglé d'articles. Épinglez vos articles préférés depuis la page principale pour les retrouver facilement ici.
+            </p>
+            <Button onClick={() => navigate("/")} variant="default">
+              Retour à l'accueil
+            </Button>
+          </div>
+        ) : (
+          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+            {pinnedArticles.map((article) => (
+              <NewsCard
+                key={article.id}
+                news={article}
+                onTogglePin={togglePin}
+                onMarkAsRead={markAsRead}
+                onDelete={deleteArticle}
+                onOpenArticle={setSelectedArticle}
+              />
+            ))}
+          </div>
+        )}
+      </main>
+
+      {/* Article Modal */}
+      {selectedArticle && (
+        <ArticleModal
+          article={selectedArticle}
+          isOpen={!!selectedArticle}
+          onClose={() => setSelectedArticle(null)}
+        />
+      )}
+    </div>
+  );
+};
+
+export default Pinned;