Merge pull request #46 from ravixalgorithm/main
feat: implement watchlist sorting by symbol
This commit is contained in:
commit
3e6545ddbe
|
|
@ -2,11 +2,10 @@ import React, { Suspense } from 'react';
|
|||
import { auth } from '@/lib/better-auth/auth';
|
||||
import { headers } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getUserWatchlist, isStockInWatchlist, removeFromWatchlist } from '@/lib/actions/watchlist.actions';
|
||||
import { getUserWatchlist } from '@/lib/actions/watchlist.actions';
|
||||
import { getUserAlerts } from '@/lib/actions/alert.actions';
|
||||
import { getNews } from '@/lib/actions/finnhub.actions';
|
||||
import TradingViewWatchlist from '@/components/watchlist/TradingViewWatchlist';
|
||||
import WatchlistStockChip from '@/components/watchlist/WatchlistStockChip';
|
||||
import WatchlistManager from '@/components/watchlist/WatchlistManager';
|
||||
import AlertsPanel from '@/components/watchlist/AlertsPanel';
|
||||
import NewsGrid from '@/components/watchlist/NewsGrid';
|
||||
import SearchCommand from '@/components/SearchCommand';
|
||||
|
|
@ -23,16 +22,14 @@ export default async function WatchlistPage() {
|
|||
|
||||
const userId = session.user.id;
|
||||
|
||||
// Parallel data fetching
|
||||
// Parallel data fetching
|
||||
const [watchlistItems, alerts, news] = await Promise.all([
|
||||
getUserWatchlist(userId),
|
||||
getUserAlerts(userId),
|
||||
getNews() // Initial news fetch, maybe refine later to use watchlist symbols
|
||||
getNews() // Initial news fetch
|
||||
]);
|
||||
|
||||
const watchlistSymbols = watchlistItems.map((item: any) => item.symbol);
|
||||
// const watchlistData = await getWatchlistData(watchlistSymbols); // OPTIMIZATION: Removed to prevent 429 errors. Widget handles data.
|
||||
|
||||
// Fallback news if watchlist has items
|
||||
const relevantNews = watchlistSymbols.length > 0 ? await getNews(watchlistSymbols) : news;
|
||||
|
|
@ -56,31 +53,7 @@ export default async function WatchlistPage() {
|
|||
{/* Main Content - Watchlist Table */}
|
||||
<div className="lg:col-span-3 space-y-8">
|
||||
<div className="space-y-6">
|
||||
{/* Manage Watchlist Section */}
|
||||
<div className="bg-gray-900/30 rounded-xl border border-gray-800 p-4 backdrop-blur-sm">
|
||||
<h3 className="text-sm font-semibold text-gray-400 mb-3 uppercase tracking-wider flex items-center">
|
||||
<span className="mr-2">Manage Symbols</span>
|
||||
<span className="text-xs bg-gray-800 text-gray-500 px-2 py-0.5 rounded-full">{watchlistSymbols.length}</span>
|
||||
</h3>
|
||||
{watchlistSymbols.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{watchlistItems.map((item: any) => (
|
||||
<WatchlistStockChip
|
||||
key={item.symbol}
|
||||
symbol={item.symbol}
|
||||
userId={userId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500 italic">No stocks in watchlist.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* TradingView Widget */}
|
||||
<div className="min-h-[550px]">
|
||||
<TradingViewWatchlist symbols={watchlistSymbols} />
|
||||
</div>
|
||||
<WatchlistManager initialItems={watchlistItems} userId={userId} />
|
||||
</div>
|
||||
|
||||
{/* News Section */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import WatchlistStockChip from './WatchlistStockChip';
|
||||
import TradingViewWatchlist from './TradingViewWatchlist';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowDownAZ, ArrowUpZA, ArrowUpDown } from 'lucide-react';
|
||||
import { WatchlistItem } from '@/database/models/watchlist.model';
|
||||
|
||||
interface WatchlistManagerProps {
|
||||
initialItems: WatchlistItem[]; // Using the DB model type directly or a simplified version
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export default function WatchlistManager({ initialItems, userId }: WatchlistManagerProps) {
|
||||
// Sort state: 'asc' (A-Z), 'desc' (Z-A), or null (added order/default)
|
||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc' | null>(null);
|
||||
|
||||
const toggleSort = () => {
|
||||
if (sortOrder === null) setSortOrder('asc');
|
||||
else if (sortOrder === 'asc') setSortOrder('desc');
|
||||
else setSortOrder(null);
|
||||
};
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
if (!sortOrder) return initialItems;
|
||||
|
||||
return [...initialItems].sort((a, b) => {
|
||||
if (sortOrder === 'asc') {
|
||||
return a.symbol.localeCompare(b.symbol);
|
||||
} else {
|
||||
return b.symbol.localeCompare(a.symbol);
|
||||
}
|
||||
});
|
||||
}, [initialItems, sortOrder]);
|
||||
|
||||
const watchlistSymbols = sortedItems.map((item) => item.symbol);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-gray-900/30 rounded-xl border border-gray-800 p-4 backdrop-blur-sm">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider flex items-center">
|
||||
<span className="mr-2">Manage Symbols</span>
|
||||
<span className="text-xs bg-gray-800 text-gray-500 px-2 py-0.5 rounded-full">
|
||||
{watchlistSymbols.length}
|
||||
</span>
|
||||
</h3>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={toggleSort}
|
||||
className="h-8 px-2 text-gray-400 hover:text-white hover:bg-white/10"
|
||||
title={
|
||||
sortOrder === 'asc'
|
||||
? 'Sorted A-Z'
|
||||
: sortOrder === 'desc'
|
||||
? 'Sorted Z-A'
|
||||
: 'Default Order'
|
||||
}
|
||||
>
|
||||
{sortOrder === 'asc' && <ArrowDownAZ className="w-4 h-4 mr-2" />}
|
||||
{sortOrder === 'desc' && <ArrowUpZA className="w-4 h-4 mr-2" />}
|
||||
{sortOrder === null && <ArrowUpDown className="w-4 h-4 mr-2" />}
|
||||
<span className="text-xs">
|
||||
{sortOrder === 'asc'
|
||||
? 'A-Z'
|
||||
: sortOrder === 'desc'
|
||||
? 'Z-A'
|
||||
: 'Sort'}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{watchlistSymbols.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{sortedItems.map((item) => (
|
||||
<WatchlistStockChip
|
||||
key={item.symbol}
|
||||
symbol={item.symbol}
|
||||
userId={userId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500 italic">No stocks in watchlist.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="min-h-[550px]">
|
||||
<TradingViewWatchlist symbols={watchlistSymbols} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue