Add initial news
This commit is contained in:
parent
21d134df1d
commit
075d949acc
1 changed files with 80 additions and 37 deletions
|
|
@ -1,41 +1,74 @@
|
|||
'use client';
|
||||
|
||||
import { Search } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
// TODO: add source with little profile pic of cnbc, bloomberg, etc.
|
||||
|
||||
export interface News {
|
||||
id: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const mockNewsData: News[] = [
|
||||
{
|
||||
"id": "_TSLA2025OUTLOOK",
|
||||
"title": "Tesla faces market challenges but bets on AI and autonomy",
|
||||
"summary": "Tesla's stock has been volatile, currently at $279.17, after a 40% decline from recent highs. Revenue growth remains stagnant, and U.S. EV market share has dropped to 44%. Elon Musk's political engagements may be affecting brand perception, with Tesla registrations in Germany falling 76%. Despite challenges, Tesla is investing in AI, robotics, and autonomous driving, aiming to launch Full Self-Driving in Austin by June 2025 and the Cybercab ride-hailing vehicle by 2026. Analysts are divided—Morgan Stanley sees growth potential, while Bank of America has lowered its price target."
|
||||
}
|
||||
,
|
||||
{ id: '_4dn88SBPLM1', title: 'Tech giants announce a new AI breakthrough', summary: 'Tech giants announce a new AI breakthrough.' },
|
||||
{ id: '_4dn88SBPLM2', title: 'Sports update: Local team secures championship win.', summary: 'Sports update: Local team secures championship win.' },
|
||||
{ id: '_4dn88SBPLM3', title: 'Market trends: Stocks surge after latest reports.', summary: 'Market trends: Stocks surge after latest reports.' },
|
||||
{ id: '_4dn88SBPLM4', title: 'Entertainment: Upcoming movies and releases for this year.', summary: 'Entertainment: Upcoming movies and releases for this year.' },
|
||||
];
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_AWS_DB_API_URL || '';
|
||||
const API_URL = `${API_BASE_URL}?queryType=joint&num_results=20`;
|
||||
const API_KEY = process.env.NEXT_PUBLIC_AWS_DB_API_KEY || '';
|
||||
|
||||
const NewsPage = () => {
|
||||
const [news] = useState<News[]>(mockNewsData);
|
||||
const NewsPageContent = () => {
|
||||
const [news, setNews] = useState<News[]>([]);
|
||||
const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({});
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchNews = async () => {
|
||||
try {
|
||||
const response = await fetch(API_URL, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch news: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
console.log("API Response:", data); // Debugging: Log the API response
|
||||
|
||||
// Convert API response into News format
|
||||
const formattedNews = data.result.map((item: any) => ({
|
||||
id: item[0], // Video ID (used in YouTube links)
|
||||
title: item[1], // News title
|
||||
description: item[3], // Short description
|
||||
summary: item[4], // Full summary (for Show More)
|
||||
}));
|
||||
|
||||
setNews(formattedNews);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'An unknown error occurred');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchNews();
|
||||
}, []);
|
||||
|
||||
const toggleSummary = (id: string) => {
|
||||
setExpanded((prev) => ({ ...prev, [id]: !prev[id] }));
|
||||
};
|
||||
|
||||
if (loading) return <p className="text-center text-lg">Loading news...</p>;
|
||||
if (error) return <p className="text-center text-lg text-red-500">Error: {error}</p>;
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="max-w-screen-xl mx-auto px-4">
|
||||
<div className="flex flex-col pt-4">
|
||||
<div className="flex items-center">
|
||||
<Search />
|
||||
|
|
@ -48,33 +81,31 @@ const NewsPage = () => {
|
|||
{news.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-800 overflow-hidden"
|
||||
className="w-full max-w-6xl mx-auto px-6 py-6 rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-800 overflow-hidden"
|
||||
>
|
||||
{/* Construct YouTube URL dynamically */}
|
||||
|
||||
<div className="flex items-start space-x-4 pt-6">
|
||||
<div className="flex flex-col lg:flex-row items-start space-y-4 lg:space-y-0 lg:space-x-4 pt-6">
|
||||
{/* Thumbnail on the Left */}
|
||||
<div className="w-1/4 aspect-[16/9] relative">
|
||||
<Link href={`https://www.youtube.com/watch?v=${item.id}`} target="_blank" className="block">
|
||||
<Image
|
||||
src="/placeholder1.jpg"
|
||||
alt="placeholder"
|
||||
fill
|
||||
className="rounded-lg object-cover"
|
||||
/>
|
||||
</Link>
|
||||
<div className="w-1/2 aspect-video">
|
||||
<iframe
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={`https://www.youtube.com/embed/${item.id}`}
|
||||
title={item.title}
|
||||
allowFullScreen
|
||||
className="rounded-lg"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
{/* Text on the Right */}
|
||||
<div className="flex-1 pt-2">
|
||||
<h2 className="text-xl font-bold text-black dark:text-white">{item.title}</h2>
|
||||
|
||||
{/* Sliced Summary */}
|
||||
<p className="text-lg text-gray-500 dark:text-gray-400 mt-2">
|
||||
{expanded[item.id] ? item.summary : `${item.summary.slice(0, 100)}...`}
|
||||
<p
|
||||
className={`text-lg text-gray-500 dark:text-gray-400 mt-2 overflow-hidden ${
|
||||
expanded[item.id] ? '' : 'line-clamp-3'
|
||||
}`}
|
||||
>
|
||||
{expanded[item.id] ? item.description : `${item.description.slice(0, 120)}...`}
|
||||
</p>
|
||||
|
||||
{/* Toggle Button */}
|
||||
<button
|
||||
className="mt-2 text-blue-500 hover:text-blue-700 text-md font-semibold"
|
||||
onClick={() => toggleSummary(item.id)}
|
||||
|
|
@ -83,6 +114,10 @@ const NewsPage = () => {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{expanded[item.id] && (
|
||||
<p className="text-md text-gray-500 dark:text-gray-400 mt-2">{item.summary}</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -90,4 +125,12 @@ const NewsPage = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const NewsPage = () => {
|
||||
return (
|
||||
<>
|
||||
<NewsPageContent />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewsPage;
|
||||
Loading…
Add table
Add a link
Reference in a new issue