This commit is contained in:
parent
03dde27de1
commit
44fd92850d
|
|
@ -30,6 +30,26 @@ class AnalysisRequest(BaseModel):
|
|||
)
|
||||
|
||||
|
||||
class PriceData(BaseModel):
|
||||
"""Stock price data model"""
|
||||
Date: str
|
||||
Open: float
|
||||
High: float
|
||||
Low: float
|
||||
Close: float
|
||||
Volume: int
|
||||
|
||||
|
||||
class PriceStats(BaseModel):
|
||||
"""Price statistics model"""
|
||||
growth_rate: float = Field(..., description="Price growth rate in percentage")
|
||||
duration_days: int = Field(..., description="Data duration in days")
|
||||
start_date: str
|
||||
end_date: str
|
||||
start_price: float
|
||||
end_price: float
|
||||
|
||||
|
||||
class AnalysisResponse(BaseModel):
|
||||
"""Response model for trading analysis"""
|
||||
status: str = Field(..., description="Analysis status (success, error, processing)")
|
||||
|
|
@ -38,6 +58,8 @@ class AnalysisResponse(BaseModel):
|
|||
decision: Optional[Dict[str, Any]] = Field(None, description="Trading decision details")
|
||||
reports: Optional[Dict[str, Any]] = Field(None, description="Analysis reports from different teams")
|
||||
error: Optional[str] = Field(None, description="Error message if analysis failed")
|
||||
price_data: Optional[List[PriceData]] = Field(None, description="Historical price data")
|
||||
price_stats: Optional[PriceStats] = Field(None, description="Price statistics")
|
||||
|
||||
|
||||
class ConfigResponse(BaseModel):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
"""
|
||||
Price data service for loading and processing stock price data
|
||||
"""
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PriceService:
|
||||
"""Service for loading and processing price data from data_cache"""
|
||||
|
||||
@staticmethod
|
||||
def load_price_data(ticker: str, data_cache_dir: str) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
Load price data from data_cache CSV files
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
data_cache_dir: Path to data cache directory
|
||||
|
||||
Returns:
|
||||
DataFrame with price data or None if not found
|
||||
"""
|
||||
try:
|
||||
cache_path = Path(data_cache_dir)
|
||||
|
||||
# Search for {ticker}-YFin-data-*.csv files
|
||||
csv_files = list(cache_path.glob(f"{ticker}-YFin-data-*.csv"))
|
||||
|
||||
if not csv_files:
|
||||
logger.warning(f"No price data found for {ticker} in {data_cache_dir}")
|
||||
return None
|
||||
|
||||
# Use the most recent file
|
||||
latest_file = max(csv_files, key=lambda p: p.stat().st_mtime)
|
||||
logger.info(f"Loading price data from {latest_file}")
|
||||
|
||||
df = pd.read_csv(latest_file)
|
||||
df['Date'] = pd.to_datetime(df['Date'])
|
||||
|
||||
return df.sort_values('Date')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading price data for {ticker}: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def calculate_stats(df: pd.DataFrame) -> Dict[str, Any]:
|
||||
"""
|
||||
Calculate price statistics
|
||||
|
||||
Args:
|
||||
df: DataFrame with price data
|
||||
|
||||
Returns:
|
||||
Dictionary with statistics
|
||||
"""
|
||||
start_price = float(df.iloc[0]['Close'])
|
||||
end_price = float(df.iloc[-1]['Close'])
|
||||
growth_rate = ((end_price - start_price) / start_price) * 100
|
||||
duration_days = (df.iloc[-1]['Date'] - df.iloc[0]['Date']).days
|
||||
|
||||
return {
|
||||
"growth_rate": round(growth_rate, 2),
|
||||
"duration_days": int(duration_days),
|
||||
"start_date": df.iloc[0]['Date'].strftime('%Y-%m-%d'),
|
||||
"end_date": df.iloc[-1]['Date'].strftime('%Y-%m-%d'),
|
||||
"start_price": round(start_price, 2),
|
||||
"end_price": round(end_price, 2),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def prepare_chart_data(df: pd.DataFrame, limit: int = 365) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Prepare price data for charting (limit to recent data)
|
||||
|
||||
Args:
|
||||
df: DataFrame with price data
|
||||
limit: Maximum number of data points to return
|
||||
|
||||
Returns:
|
||||
List of dictionaries with price data
|
||||
"""
|
||||
# Get recent data
|
||||
recent_df = df.tail(limit)
|
||||
|
||||
# Convert to list of dicts
|
||||
data = []
|
||||
for _, row in recent_df.iterrows():
|
||||
data.append({
|
||||
"Date": row['Date'].strftime('%Y-%m-%d'),
|
||||
"Open": round(float(row['Open']), 2),
|
||||
"High": round(float(row['High']), 2),
|
||||
"Low": round(float(row['Low']), 2),
|
||||
"Close": round(float(row['Close']), 2),
|
||||
"Volume": int(row['Volume']),
|
||||
})
|
||||
|
||||
return data
|
||||
|
|
@ -111,12 +111,28 @@ class TradingService:
|
|||
"risk_debate_state": final_state.get("risk_debate_state"),
|
||||
}
|
||||
|
||||
# Load price data
|
||||
from backend.app.services.price_service import PriceService
|
||||
price_data = None
|
||||
price_stats = None
|
||||
|
||||
try:
|
||||
price_df = PriceService.load_price_data(ticker, config.get("data_cache_dir"))
|
||||
if price_df is not None:
|
||||
price_data = PriceService.prepare_chart_data(price_df)
|
||||
price_stats = PriceService.calculate_stats(price_df)
|
||||
logger.info(f"Loaded {len(price_data)} price data points for {ticker}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load price data for {ticker}: {e}")
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"ticker": ticker,
|
||||
"analysis_date": analysis_date,
|
||||
"decision": decision,
|
||||
"reports": reports,
|
||||
"price_data": price_data,
|
||||
"price_stats": price_stats,
|
||||
}
|
||||
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useState } from "react";
|
|||
import { AnalysisForm } from "@/components/analysis/AnalysisForm";
|
||||
import { TradingDecision } from "@/components/analysis/TradingDecision";
|
||||
import { AnalystReport } from "@/components/analysis/AnalystReport";
|
||||
import { PriceChart } from "@/components/analysis/PriceChart";
|
||||
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
|
||||
import { useAnalysis } from "@/hooks/useAnalysis";
|
||||
import type { AnalysisRequest } from "@/lib/types";
|
||||
|
|
@ -48,7 +49,19 @@ export default function AnalysisPage() {
|
|||
|
||||
{result && !loading && (
|
||||
<div className="space-y-8">
|
||||
{/* 價格圖表 */}
|
||||
{result.price_data && result.price_stats && (
|
||||
<PriceChart
|
||||
priceData={result.price_data}
|
||||
priceStats={result.price_stats}
|
||||
ticker={result.ticker}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 交易決策 */}
|
||||
<TradingDecision result={result} />
|
||||
|
||||
{/* 分析報告 */}
|
||||
{result.reports && <AnalystReport reports={result.reports} />}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -33,6 +34,7 @@ import type { AnalysisRequest } from "@/lib/types";
|
|||
const formSchema = z.object({
|
||||
ticker: z.string().min(1, "股票代碼為必填").max(10),
|
||||
analysis_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "日期格式必須為 YYYY-MM-DD"),
|
||||
analysts: z.array(z.string()).min(1, "請至少選擇一位分析師"),
|
||||
research_depth: z.number().min(1).max(5),
|
||||
deep_think_llm: z.string(),
|
||||
quick_think_llm: z.string(),
|
||||
|
|
@ -48,12 +50,20 @@ interface AnalysisFormProps {
|
|||
loading?: boolean;
|
||||
}
|
||||
|
||||
const ANALYSTS = [
|
||||
{ value: "market", label: "市場分析師" },
|
||||
{ value: "social", label: "社群媒體分析師" },
|
||||
{ value: "news", label: "新聞分析師" },
|
||||
{ value: "fundamentals", label: "基本面分析師" },
|
||||
];
|
||||
|
||||
export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
ticker: "NVDA",
|
||||
analysis_date: format(new Date(), "yyyy-MM-dd"),
|
||||
analysts: ["market", "social", "news", "fundamentals"], // 預設全選
|
||||
research_depth: 1,
|
||||
deep_think_llm: "gpt-4o-mini",
|
||||
quick_think_llm: "gpt-4o-mini",
|
||||
|
|
@ -63,10 +73,19 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
},
|
||||
});
|
||||
|
||||
// 全選/取消全選
|
||||
const toggleSelectAll = () => {
|
||||
const currentAnalysts = form.getValues("analysts");
|
||||
if (currentAnalysts.length === ANALYSTS.length) {
|
||||
form.setValue("analysts", []);
|
||||
} else {
|
||||
form.setValue("analysts", ANALYSTS.map(a => a.value));
|
||||
}
|
||||
};
|
||||
|
||||
function handleSubmit(values: z.infer<typeof formSchema>) {
|
||||
const request: AnalysisRequest = {
|
||||
...values,
|
||||
analysts: ["market", "sentiment", "news", "fundamentals"],
|
||||
};
|
||||
onSubmit(request);
|
||||
}
|
||||
|
|
@ -83,6 +102,65 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* 分析師選擇區塊 */}
|
||||
<div className="md:col-span-2 border-b pb-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<FormLabel className="text-base font-semibold">分析師團隊</FormLabel>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={toggleSelectAll}
|
||||
>
|
||||
{form.watch("analysts").length === ANALYSTS.length ? "取消全選" : "全選"}
|
||||
</Button>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="analysts"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{ANALYSTS.map((analyst) => (
|
||||
<FormField
|
||||
key={analyst.value}
|
||||
control={form.control}
|
||||
name="analysts"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={analyst.value}
|
||||
className="flex flex-row items-start space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(analyst.value)}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, analyst.value])
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value: string) => value !== analyst.value
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal cursor-pointer">
|
||||
{analyst.label}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="ticker"
|
||||
|
|
@ -133,9 +211,11 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) {
|
|||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">淺層 - 快速研究,較少的辯論和策略討論</SelectItem>
|
||||
<SelectItem value="3">中等 - 中等程度,適度的辯論和策略討論</SelectItem>
|
||||
<SelectItem value="5">深層 - 全面研究,深入的辯論和策略討論</SelectItem>
|
||||
<SelectItem value="1">1 - 快速</SelectItem>
|
||||
<SelectItem value="2">2 - 標準</SelectItem>
|
||||
<SelectItem value="3">3 - 詳盡</SelectItem>
|
||||
<SelectItem value="4">4 - 深入</SelectItem>
|
||||
<SelectItem value="5">5 - 全面</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
Area,
|
||||
} from "recharts";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import type { PriceData, PriceStats } from "@/lib/types";
|
||||
|
||||
interface PriceChartProps {
|
||||
priceData: PriceData[];
|
||||
priceStats: PriceStats;
|
||||
ticker: string;
|
||||
}
|
||||
|
||||
export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
||||
const [chartType, setChartType] = useState<"line" | "candlestick">("line");
|
||||
|
||||
// 格式化數字
|
||||
const formatNumber = (num: number) => {
|
||||
return num.toLocaleString('zh-TW', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
};
|
||||
|
||||
// 格式化日期(只顯示月-日)
|
||||
const formatDate = (dateStr: string) => {
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getMonth() + 1}/${date.getDate()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<div className="flex justify-between items-center">
|
||||
<CardTitle className="text-2xl">{ticker} 價格走勢</CardTitle>
|
||||
<Tabs value={chartType} onValueChange={(v) => setChartType(v as "line" | "candlestick")}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="line">折線圖</TabsTrigger>
|
||||
<TabsTrigger value="candlestick">K線圖</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
{/* 統計資訊 */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mt-4">
|
||||
<div className="bg-muted p-4 rounded-lg">
|
||||
<p className="text-sm text-muted-foreground">增長率</p>
|
||||
<p className={`text-2xl font-bold ${priceStats.growth_rate >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{priceStats.growth_rate >= 0 ? '+' : ''}{priceStats.growth_rate}%
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-muted p-4 rounded-lg">
|
||||
<p className="text-sm text-muted-foreground">時長</p>
|
||||
<p className="text-2xl font-bold">{priceStats.duration_days} 天</p>
|
||||
</div>
|
||||
<div className="bg-muted p-4 rounded-lg">
|
||||
<p className="text-sm text-muted-foreground">起始價格</p>
|
||||
<p className="text-lg font-semibold">${formatNumber(priceStats.start_price)}</p>
|
||||
<p className="text-xs text-muted-foreground">{priceStats.start_date}</p>
|
||||
</div>
|
||||
<div className="bg-muted p-4 rounded-lg">
|
||||
<p className="text-sm text-muted-foreground">結束價格</p>
|
||||
<p className="text-lg font-semibold">${formatNumber(priceStats.end_price)}</p>
|
||||
<p className="text-xs text-muted-foreground">{priceStats.end_date}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="space-y-6">
|
||||
{/* 價格圖表 */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">價格走勢</h3>
|
||||
<ResponsiveContainer width="100%" height={400}>
|
||||
{chartType === "line" ? (
|
||||
<LineChart data={priceData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
dataKey="Date"
|
||||
tickFormatter={formatDate}
|
||||
minTickGap={30}
|
||||
/>
|
||||
<YAxis
|
||||
domain={['auto', 'auto']}
|
||||
tickFormatter={(value) => `$${value.toFixed(0)}`}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number) => [`$${formatNumber(value)}`, '收盤價']}
|
||||
labelFormatter={(label) => `日期: ${label}`}
|
||||
/>
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="Close"
|
||||
stroke="#2563eb"
|
||||
strokeWidth={2}
|
||||
name="收盤價"
|
||||
dot={false}
|
||||
/>
|
||||
</LineChart>
|
||||
) : (
|
||||
<ComposedChart data={priceData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
dataKey="Date"
|
||||
tickFormatter={formatDate}
|
||||
minTickGap={30}
|
||||
/>
|
||||
<YAxis
|
||||
domain={['auto', 'auto']}
|
||||
tickFormatter={(value) => `$${value.toFixed(0)}`}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number, name: string) => [`$${formatNumber(value)}`, name]}
|
||||
labelFormatter={(label) => `日期: ${label}`}
|
||||
/>
|
||||
<Legend />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="High"
|
||||
stroke="#86efac"
|
||||
fill="#86efac"
|
||||
fillOpacity={0.3}
|
||||
name="最高價"
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="Low"
|
||||
stroke="#fca5a5"
|
||||
fill="#fca5a5"
|
||||
fillOpacity={0.3}
|
||||
name="最低價"
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="Open"
|
||||
stroke="#f59e0b"
|
||||
strokeWidth={2}
|
||||
name="開盤價"
|
||||
dot={false}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="Close"
|
||||
stroke="#2563eb"
|
||||
strokeWidth={2}
|
||||
name="收盤價"
|
||||
dot={false}
|
||||
/>
|
||||
</ComposedChart>
|
||||
)}
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* 交易量圖表 */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">交易量</h3>
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<BarChart data={priceData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
dataKey="Date"
|
||||
tickFormatter={formatDate}
|
||||
minTickGap={30}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormatter={(value) => {
|
||||
if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
|
||||
if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;
|
||||
return value.toString();
|
||||
}}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value: number) => [value.toLocaleString(), '交易量']}
|
||||
labelFormatter={(label) => `日期: ${label}`}
|
||||
/>
|
||||
<Bar dataKey="Volume" fill="#10b981" name="交易量" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
data-slot="checkbox-indicator"
|
||||
className="grid place-content-center text-current transition-none"
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox }
|
||||
|
|
@ -16,13 +16,33 @@ export interface AnalysisRequest {
|
|||
alpha_vantage_api_key?: string;
|
||||
}
|
||||
|
||||
export interface PriceData {
|
||||
Date: string;
|
||||
Open: number;
|
||||
High: number;
|
||||
Low: number;
|
||||
Close: number;
|
||||
Volume: number;
|
||||
}
|
||||
|
||||
export interface PriceStats {
|
||||
growth_rate: number;
|
||||
duration_days: number;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
start_price: number;
|
||||
end_price: number;
|
||||
}
|
||||
|
||||
export interface AnalysisResponse {
|
||||
status: "success" | "error" | "processing";
|
||||
status: string;
|
||||
ticker: string;
|
||||
analysis_date: string;
|
||||
decision?: Decision;
|
||||
reports?: Reports;
|
||||
decision?: any;
|
||||
reports?: any;
|
||||
error?: string;
|
||||
price_data?: PriceData[];
|
||||
price_stats?: PriceStats;
|
||||
}
|
||||
|
||||
export interface Decision {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-hook-form": "^7.66.1",
|
||||
"recharts": "^3.4.1",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ importers:
|
|||
'@hookform/resolvers':
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2(react-hook-form@7.66.1(react@19.2.0))
|
||||
'@radix-ui/react-checkbox':
|
||||
specifier: ^1.3.3
|
||||
version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.15
|
||||
version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
|
|
@ -53,6 +56,9 @@ importers:
|
|||
react-hook-form:
|
||||
specifier: ^7.66.1
|
||||
version: 7.66.1(react@19.2.0)
|
||||
recharts:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1)
|
||||
tailwind-merge:
|
||||
specifier: ^3.4.0
|
||||
version: 3.4.0
|
||||
|
|
@ -492,6 +498,19 @@ packages:
|
|||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-checkbox@1.3.3':
|
||||
resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-collection@1.1.7':
|
||||
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
|
||||
peerDependencies:
|
||||
|
|
@ -812,9 +831,23 @@ packages:
|
|||
'@radix-ui/rect@1.1.1':
|
||||
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
|
||||
|
||||
'@reduxjs/toolkit@2.10.1':
|
||||
resolution: {integrity: sha512-/U17EXQ9Do9Yx4DlNGU6eVNfZvFJfYpUtRRdLf19PbPjdWBxNlxGZXywQZ1p1Nz8nMkWplTI7iD/23m07nolDA==}
|
||||
peerDependencies:
|
||||
react: ^16.9.0 || ^17.0.0 || ^18 || ^19
|
||||
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
react-redux:
|
||||
optional: true
|
||||
|
||||
'@rtsao/scc@1.1.0':
|
||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||
|
||||
'@standard-schema/spec@1.0.0':
|
||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||
|
||||
'@standard-schema/utils@0.3.0':
|
||||
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
|
||||
|
||||
|
|
@ -912,6 +945,33 @@ packages:
|
|||
'@tybys/wasm-util@0.10.1':
|
||||
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
|
||||
|
||||
'@types/d3-array@3.2.2':
|
||||
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
|
||||
|
||||
'@types/d3-color@3.1.3':
|
||||
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
|
||||
|
||||
'@types/d3-ease@3.0.2':
|
||||
resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
|
||||
|
||||
'@types/d3-path@3.1.1':
|
||||
resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
|
||||
|
||||
'@types/d3-scale@4.0.9':
|
||||
resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
|
||||
|
||||
'@types/d3-shape@3.1.7':
|
||||
resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
|
||||
|
||||
'@types/d3-time@3.0.4':
|
||||
resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
|
||||
|
||||
'@types/d3-timer@3.0.2':
|
||||
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
|
|
@ -932,6 +992,9 @@ packages:
|
|||
'@types/react@19.2.6':
|
||||
resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==}
|
||||
|
||||
'@types/use-sync-external-store@0.0.6':
|
||||
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.47.0':
|
||||
resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -1253,6 +1316,50 @@ packages:
|
|||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
d3-array@3.2.4:
|
||||
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-format@3.1.0:
|
||||
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-path@3.1.0:
|
||||
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-scale@4.0.2:
|
||||
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-shape@3.2.0:
|
||||
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-time-format@4.1.0:
|
||||
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-time@3.1.0:
|
||||
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
damerau-levenshtein@1.0.8:
|
||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||
|
||||
|
|
@ -1288,6 +1395,9 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
decimal.js-light@2.5.1:
|
||||
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
|
||||
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
|
|
@ -1360,6 +1470,9 @@ packages:
|
|||
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-toolkit@1.42.0:
|
||||
resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==}
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -1484,6 +1597,9 @@ packages:
|
|||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
|
|
@ -1657,6 +1773,9 @@ packages:
|
|||
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
immer@10.2.0:
|
||||
resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -1669,6 +1788,10 @@ packages:
|
|||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
internmap@2.0.3:
|
||||
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-array-buffer@3.0.5:
|
||||
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -2113,6 +2236,18 @@ packages:
|
|||
react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
react-redux@9.2.0:
|
||||
resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==}
|
||||
peerDependencies:
|
||||
'@types/react': ^18.2.25 || ^19
|
||||
react: ^18.0 || ^19
|
||||
redux: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
redux:
|
||||
optional: true
|
||||
|
||||
react-remove-scroll-bar@2.3.8:
|
||||
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -2147,6 +2282,22 @@ packages:
|
|||
resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
recharts@3.4.1:
|
||||
resolution: {integrity: sha512-35kYg6JoOgwq8sE4rhYkVWwa6aAIgOtT+Ob0gitnShjwUwZmhrmy7Jco/5kJNF4PnLXgt9Hwq+geEMS+WrjU1g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
redux-thunk@3.1.0:
|
||||
resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
|
||||
peerDependencies:
|
||||
redux: ^5.0.0
|
||||
|
||||
redux@5.0.1:
|
||||
resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -2155,6 +2306,9 @@ packages:
|
|||
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
reselect@5.1.1:
|
||||
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
||||
|
||||
resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -2315,6 +2469,9 @@ packages:
|
|||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
|
@ -2409,6 +2566,14 @@ packages:
|
|||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-sync-external-store@1.6.0:
|
||||
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
victory-vendor@37.3.6:
|
||||
resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
|
||||
|
||||
which-boxed-primitive@1.1.1:
|
||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -2829,6 +2994,22 @@ snapshots:
|
|||
'@types/react': 19.2.6
|
||||
'@types/react-dom': 19.2.3(@types/react@19.2.6)
|
||||
|
||||
'@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.6)(react@19.2.0)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.6)(react@19.2.0)
|
||||
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.6)(react@19.2.0)
|
||||
'@radix-ui/react-use-size': 1.1.1(@types/react@19.2.6)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.6
|
||||
'@types/react-dom': 19.2.3(@types/react@19.2.6)
|
||||
|
||||
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.6))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.6)(react@19.2.0)
|
||||
|
|
@ -3124,8 +3305,22 @@ snapshots:
|
|||
|
||||
'@radix-ui/rect@1.1.1': {}
|
||||
|
||||
'@reduxjs/toolkit@2.10.1(react-redux@9.2.0(@types/react@19.2.6)(react@19.2.0)(redux@5.0.1))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.0.0
|
||||
'@standard-schema/utils': 0.3.0
|
||||
immer: 10.2.0
|
||||
redux: 5.0.1
|
||||
redux-thunk: 3.1.0(redux@5.0.1)
|
||||
reselect: 5.1.1
|
||||
optionalDependencies:
|
||||
react: 19.2.0
|
||||
react-redux: 9.2.0(@types/react@19.2.6)(react@19.2.0)(redux@5.0.1)
|
||||
|
||||
'@rtsao/scc@1.1.0': {}
|
||||
|
||||
'@standard-schema/spec@1.0.0': {}
|
||||
|
||||
'@standard-schema/utils@0.3.0': {}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
|
|
@ -3206,6 +3401,30 @@ snapshots:
|
|||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/d3-array@3.2.2': {}
|
||||
|
||||
'@types/d3-color@3.1.3': {}
|
||||
|
||||
'@types/d3-ease@3.0.2': {}
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
dependencies:
|
||||
'@types/d3-color': 3.1.3
|
||||
|
||||
'@types/d3-path@3.1.1': {}
|
||||
|
||||
'@types/d3-scale@4.0.9':
|
||||
dependencies:
|
||||
'@types/d3-time': 3.0.4
|
||||
|
||||
'@types/d3-shape@3.1.7':
|
||||
dependencies:
|
||||
'@types/d3-path': 3.1.1
|
||||
|
||||
'@types/d3-time@3.0.4': {}
|
||||
|
||||
'@types/d3-timer@3.0.2': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
|
@ -3224,6 +3443,8 @@ snapshots:
|
|||
dependencies:
|
||||
csstype: 3.2.3
|
||||
|
||||
'@types/use-sync-external-store@0.0.6': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
|
|
@ -3575,6 +3796,44 @@ snapshots:
|
|||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
d3-array@3.2.4:
|
||||
dependencies:
|
||||
internmap: 2.0.3
|
||||
|
||||
d3-color@3.1.0: {}
|
||||
|
||||
d3-ease@3.0.1: {}
|
||||
|
||||
d3-format@3.1.0: {}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
|
||||
d3-path@3.1.0: {}
|
||||
|
||||
d3-scale@4.0.2:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
d3-format: 3.1.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-time: 3.1.0
|
||||
d3-time-format: 4.1.0
|
||||
|
||||
d3-shape@3.2.0:
|
||||
dependencies:
|
||||
d3-path: 3.1.0
|
||||
|
||||
d3-time-format@4.1.0:
|
||||
dependencies:
|
||||
d3-time: 3.1.0
|
||||
|
||||
d3-time@3.1.0:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
|
||||
d3-timer@3.0.1: {}
|
||||
|
||||
damerau-levenshtein@1.0.8: {}
|
||||
|
||||
data-view-buffer@1.0.2:
|
||||
|
|
@ -3605,6 +3864,8 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decimal.js-light@2.5.1: {}
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
|
|
@ -3745,6 +4006,8 @@ snapshots:
|
|||
is-date-object: 1.1.0
|
||||
is-symbol: 1.1.1
|
||||
|
||||
es-toolkit@1.42.0: {}
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
|
@ -3952,6 +4215,8 @@ snapshots:
|
|||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-glob@3.3.1:
|
||||
|
|
@ -4118,6 +4383,8 @@ snapshots:
|
|||
|
||||
ignore@7.0.5: {}
|
||||
|
||||
immer@10.2.0: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
dependencies:
|
||||
parent-module: 1.0.1
|
||||
|
|
@ -4131,6 +4398,8 @@ snapshots:
|
|||
hasown: 2.0.2
|
||||
side-channel: 1.1.0
|
||||
|
||||
internmap@2.0.3: {}
|
||||
|
||||
is-array-buffer@3.0.5:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
|
|
@ -4550,6 +4819,15 @@ snapshots:
|
|||
|
||||
react-is@16.13.1: {}
|
||||
|
||||
react-redux@9.2.0(@types/react@19.2.6)(react@19.2.0)(redux@5.0.1):
|
||||
dependencies:
|
||||
'@types/use-sync-external-store': 0.0.6
|
||||
react: 19.2.0
|
||||
use-sync-external-store: 1.6.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.6
|
||||
redux: 5.0.1
|
||||
|
||||
react-remove-scroll-bar@2.3.8(@types/react@19.2.6)(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
|
@ -4579,6 +4857,32 @@ snapshots:
|
|||
|
||||
react@19.2.0: {}
|
||||
|
||||
recharts@3.4.1(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(redux@5.0.1):
|
||||
dependencies:
|
||||
'@reduxjs/toolkit': 2.10.1(react-redux@9.2.0(@types/react@19.2.6)(react@19.2.0)(redux@5.0.1))(react@19.2.0)
|
||||
clsx: 2.1.1
|
||||
decimal.js-light: 2.5.1
|
||||
es-toolkit: 1.42.0
|
||||
eventemitter3: 5.0.1
|
||||
immer: 10.2.0
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
react-is: 16.13.1
|
||||
react-redux: 9.2.0(@types/react@19.2.6)(react@19.2.0)(redux@5.0.1)
|
||||
reselect: 5.1.1
|
||||
tiny-invariant: 1.3.3
|
||||
use-sync-external-store: 1.6.0(react@19.2.0)
|
||||
victory-vendor: 37.3.6
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- redux
|
||||
|
||||
redux-thunk@3.1.0(redux@5.0.1):
|
||||
dependencies:
|
||||
redux: 5.0.1
|
||||
|
||||
redux@5.0.1: {}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
|
|
@ -4599,6 +4903,8 @@ snapshots:
|
|||
gopd: 1.2.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
reselect@5.1.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
|
@ -4816,6 +5122,8 @@ snapshots:
|
|||
|
||||
tapable@2.3.0: {}
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
|
|
@ -4948,6 +5256,27 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/react': 19.2.6
|
||||
|
||||
use-sync-external-store@1.6.0(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
victory-vendor@37.3.6:
|
||||
dependencies:
|
||||
'@types/d3-array': 3.2.2
|
||||
'@types/d3-ease': 3.0.2
|
||||
'@types/d3-interpolate': 3.0.4
|
||||
'@types/d3-scale': 4.0.9
|
||||
'@types/d3-shape': 3.1.7
|
||||
'@types/d3-time': 3.0.4
|
||||
'@types/d3-timer': 3.0.2
|
||||
d3-array: 3.2.4
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-scale: 4.0.2
|
||||
d3-shape: 3.2.0
|
||||
d3-time: 3.1.0
|
||||
d3-timer: 3.0.1
|
||||
|
||||
which-boxed-primitive@1.1.1:
|
||||
dependencies:
|
||||
is-bigint: 1.1.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue