From 0eee17a43e54b47e6a1da2d9202ca8aec6ca2026 Mon Sep 17 00:00:00 2001 From: kimjaehyeon0101 <47347352-kimjaehyeon0101@users.noreply.replit.com> Date: Wed, 1 Oct 2025 06:07:35 +0000 Subject: [PATCH] Add a visual sparkle effect for new news updates on the Erling Haaland media outlet Introduces a new API endpoint `/api/articles` and implements a client-side animation to visually alert users of new articles on the 'erling-haaland' outlet using CSS keyframes and conditional rendering. Replit-Commit-Author: Agent Replit-Commit-Session-Id: d9e77062-eeec-4c95-9131-905f69a78072 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/d9e77062-eeec-4c95-9131-905f69a78072/VftcvwB --- .replit | 4 +++ client/src/components/MainContent.tsx | 28 ++++++++++++++++++-- client/src/index.css | 38 +++++++++++++++++++++++++++ server/routes.ts | 10 +++++++ server/storage.ts | 8 ++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/.replit b/.replit index e53b196..bb783a3 100644 --- a/.replit +++ b/.replit @@ -30,6 +30,10 @@ externalPort = 3003 localPort = 43349 externalPort = 3000 +[[ports]] +localPort = 43777 +externalPort = 4200 + [env] PORT = "5000" diff --git a/client/src/components/MainContent.tsx b/client/src/components/MainContent.tsx index 97e6297..df444ec 100644 --- a/client/src/components/MainContent.tsx +++ b/client/src/components/MainContent.tsx @@ -74,6 +74,7 @@ export default function MainContent() { const articleCount = articleCountByOutlet[outlet.id] || 0; const hasArticles = articleCount > 0; const isErlingHaaland = outlet.slug === 'erling-haaland'; + const showSpecialEffect = isErlingHaaland && hasArticles; const animationClass = hasArticles ? 'animate-float ring-2 ring-blue-400/30 shadow-lg' @@ -87,13 +88,36 @@ export default function MainContent() { > {hasArticles && (
- + NEW + {showSpecialEffect && ( +
+ {[...Array(6)].map((_, i) => { + const angle = (i * 60) * Math.PI / 180; + const distance = 20 + (i % 2) * 10; + const x = Math.cos(angle) * distance; + const y = Math.sin(angle) * distance; + const delay = i * 0.2; + + return ( +
+ ); + })} +
+ )}
)} - +
{ }); // Article routes + app.get('/api/articles', async (req, res) => { + try { + const articles = await storage.getArticles(); + res.json(articles); + } catch (error) { + console.error("Error fetching articles:", error); + res.status(500).json({ message: "Failed to fetch articles" }); + } + }); + app.get('/api/media-outlets/:slug/articles', async (req, res) => { try { const outlet = await storage.getMediaOutletBySlug(req.params.slug); diff --git a/server/storage.ts b/server/storage.ts index f2461c2..93a4cf3 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -43,6 +43,7 @@ export interface IStorage { updateMediaOutlet(id: string, outlet: Partial): Promise; // Article operations + getArticles(): Promise; getArticlesByOutlet(mediaOutletId: string): Promise; getArticleBySlug(slug: string): Promise
; createArticle(article: InsertArticle): Promise
; @@ -144,6 +145,13 @@ export class DatabaseStorage implements IStorage { } // Article operations + async getArticles(): Promise { + return await db + .select() + .from(articles) + .orderBy(desc(articles.publishedAt)); + } + async getArticlesByOutlet(mediaOutletId: string): Promise { return await db .select()