mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 14:04:08 +01:00
ui: add placeholder for marketplace and chatbot
This commit is contained in:
parent
ba4c2ce1b4
commit
aaea2feef7
693
frontend/app/(sidebar)/chatbot/page.tsx
Normal file
693
frontend/app/(sidebar)/chatbot/page.tsx
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
Send,
|
||||||
|
Clock,
|
||||||
|
X,
|
||||||
|
Leaf,
|
||||||
|
MessageSquare,
|
||||||
|
History,
|
||||||
|
PanelRightClose,
|
||||||
|
PanelRightOpen,
|
||||||
|
Search,
|
||||||
|
Sparkles,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { Avatar } from "@/components/ui/avatar";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Card } from "@/components/ui/card";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import type { Farm, Crop } from "@/types";
|
||||||
|
|
||||||
|
// Mock data for farms and crops
|
||||||
|
const mockFarms: Farm[] = [
|
||||||
|
{
|
||||||
|
id: "farm1",
|
||||||
|
name: "Green Valley Farm",
|
||||||
|
location: "California",
|
||||||
|
type: "Organic",
|
||||||
|
createdAt: new Date("2023-01-15"),
|
||||||
|
area: "120 acres",
|
||||||
|
crops: 8,
|
||||||
|
weather: {
|
||||||
|
temperature: 24,
|
||||||
|
humidity: 65,
|
||||||
|
rainfall: "2mm",
|
||||||
|
sunlight: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "farm2",
|
||||||
|
name: "Sunrise Fields",
|
||||||
|
location: "Iowa",
|
||||||
|
type: "Conventional",
|
||||||
|
createdAt: new Date("2022-11-05"),
|
||||||
|
area: "350 acres",
|
||||||
|
crops: 5,
|
||||||
|
weather: {
|
||||||
|
temperature: 22,
|
||||||
|
humidity: 58,
|
||||||
|
rainfall: "0mm",
|
||||||
|
sunlight: 90,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockCrops: Crop[] = [
|
||||||
|
{
|
||||||
|
id: "crop1",
|
||||||
|
farmId: "farm1",
|
||||||
|
name: "Organic Tomatoes",
|
||||||
|
plantedDate: new Date("2023-03-10"),
|
||||||
|
status: "Growing",
|
||||||
|
variety: "Roma",
|
||||||
|
area: "15 acres",
|
||||||
|
healthScore: 92,
|
||||||
|
progress: 65,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "crop2",
|
||||||
|
farmId: "farm1",
|
||||||
|
name: "Sweet Corn",
|
||||||
|
plantedDate: new Date("2023-04-05"),
|
||||||
|
status: "Growing",
|
||||||
|
variety: "Golden Bantam",
|
||||||
|
area: "25 acres",
|
||||||
|
healthScore: 88,
|
||||||
|
progress: 45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "crop3",
|
||||||
|
farmId: "farm2",
|
||||||
|
name: "Soybeans",
|
||||||
|
plantedDate: new Date("2023-05-15"),
|
||||||
|
status: "Growing",
|
||||||
|
variety: "Pioneer",
|
||||||
|
area: "120 acres",
|
||||||
|
healthScore: 95,
|
||||||
|
progress: 30,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock chat history
|
||||||
|
interface ChatMessage {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
sender: "user" | "bot";
|
||||||
|
timestamp: Date;
|
||||||
|
relatedTo?: {
|
||||||
|
type: "farm" | "crop";
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockChatHistory: ChatMessage[] = [
|
||||||
|
{
|
||||||
|
id: "msg1",
|
||||||
|
content: "When should I harvest my tomatoes?",
|
||||||
|
sender: "user",
|
||||||
|
timestamp: new Date("2023-07-15T10:30:00"),
|
||||||
|
relatedTo: {
|
||||||
|
type: "crop",
|
||||||
|
id: "crop1",
|
||||||
|
name: "Organic Tomatoes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "msg2",
|
||||||
|
content:
|
||||||
|
"Based on the current growth stage of your Roma tomatoes, they should be ready for harvest in approximately 2-3 weeks. The ideal time to harvest is when they've developed their full red color but are still firm to the touch. Keep monitoring the soil moisture levels as consistent watering during the final ripening stage is crucial for flavor development.",
|
||||||
|
sender: "bot",
|
||||||
|
timestamp: new Date("2023-07-15T10:30:30"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "msg3",
|
||||||
|
content: "What's the best fertilizer for corn?",
|
||||||
|
sender: "user",
|
||||||
|
timestamp: new Date("2023-07-16T14:22:00"),
|
||||||
|
relatedTo: {
|
||||||
|
type: "crop",
|
||||||
|
id: "crop2",
|
||||||
|
name: "Sweet Corn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "msg4",
|
||||||
|
content:
|
||||||
|
"For your Sweet Corn at Green Valley Farm, I recommend a nitrogen-rich fertilizer with an NPK ratio of approximately 16-4-8. Corn is a heavy nitrogen feeder, especially during its growth phase. Apply the fertilizer when the plants are knee-high and again when they begin to tassel. Based on your soil analysis, consider supplementing with sulfur to address the slight deficiency detected in your last soil test.",
|
||||||
|
sender: "bot",
|
||||||
|
timestamp: new Date("2023-07-16T14:22:45"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Recommended prompts
|
||||||
|
const recommendedPrompts = [
|
||||||
|
{
|
||||||
|
id: "prompt1",
|
||||||
|
text: "When should I water my crops?",
|
||||||
|
category: "Irrigation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "prompt2",
|
||||||
|
text: "How can I improve soil health?",
|
||||||
|
category: "Soil Management",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "prompt3",
|
||||||
|
text: "What pests might affect my crops this season?",
|
||||||
|
category: "Pest Control",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "prompt4",
|
||||||
|
text: "Recommend a crop rotation plan",
|
||||||
|
category: "Planning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "prompt5",
|
||||||
|
text: "How to maximize yield for my current crops?",
|
||||||
|
category: "Optimization",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "prompt6",
|
||||||
|
text: "What's the best time to harvest?",
|
||||||
|
category: "Harvesting",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ChatbotPage() {
|
||||||
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||||
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
const [isHistoryOpen, setIsHistoryOpen] = useState(false);
|
||||||
|
const [selectedFarm, setSelectedFarm] = useState<string | null>(null);
|
||||||
|
const [selectedCrop, setSelectedCrop] = useState<string | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// Initialize with a welcome message
|
||||||
|
useEffect(() => {
|
||||||
|
setMessages([
|
||||||
|
{
|
||||||
|
id: "welcome",
|
||||||
|
content:
|
||||||
|
"👋 Hello! I'm ForFarm Assistant, your farming AI companion. How can I help you today? You can ask me about crop management, pest control, weather impacts, or select a specific farm or crop to get tailored advice.",
|
||||||
|
sender: "bot",
|
||||||
|
timestamp: new Date(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Scroll to bottom of messages
|
||||||
|
useEffect(() => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
// Filter crops based on selected farm
|
||||||
|
const filteredCrops = selectedFarm ? mockCrops.filter((crop) => crop.farmId === selectedFarm) : mockCrops;
|
||||||
|
|
||||||
|
// Handle sending a message
|
||||||
|
const handleSendMessage = (content: string = inputValue) => {
|
||||||
|
if (!content.trim()) return;
|
||||||
|
|
||||||
|
// Create user message
|
||||||
|
const userMessage: ChatMessage = {
|
||||||
|
id: `user-${Date.now()}`,
|
||||||
|
content,
|
||||||
|
sender: "user",
|
||||||
|
timestamp: new Date(),
|
||||||
|
...(selectedFarm || selectedCrop
|
||||||
|
? {
|
||||||
|
relatedTo: {
|
||||||
|
type: selectedCrop ? "crop" : "farm",
|
||||||
|
id: selectedCrop || selectedFarm || "",
|
||||||
|
name: selectedCrop
|
||||||
|
? mockCrops.find((c) => c.id === selectedCrop)?.name || ""
|
||||||
|
: mockFarms.find((f) => f.id === selectedFarm)?.name || "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages((prev) => [...prev, userMessage]);
|
||||||
|
setInputValue("");
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// Simulate bot response after a delay
|
||||||
|
setTimeout(() => {
|
||||||
|
const botResponse: ChatMessage = {
|
||||||
|
id: `bot-${Date.now()}`,
|
||||||
|
content: generateBotResponse(content, selectedFarm, selectedCrop),
|
||||||
|
sender: "bot",
|
||||||
|
timestamp: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
setMessages((prev) => [...prev, botResponse]);
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 1500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate a bot response based on the user's message and selected farm/crop
|
||||||
|
const generateBotResponse = (message: string, farmId: string | null, cropId: string | null): string => {
|
||||||
|
const lowerMessage = message.toLowerCase();
|
||||||
|
|
||||||
|
// Get farm and crop details if selected
|
||||||
|
const farm = farmId ? mockFarms.find((f) => f.id === farmId) : null;
|
||||||
|
const crop = cropId ? mockCrops.find((c) => c.id === cropId) : null;
|
||||||
|
|
||||||
|
// Personalize response based on selected farm/crop
|
||||||
|
let contextPrefix = "";
|
||||||
|
if (crop) {
|
||||||
|
contextPrefix = `For your ${crop.name} (${crop.variety}) at ${farm?.name || "your farm"}, `;
|
||||||
|
} else if (farm) {
|
||||||
|
contextPrefix = `For ${farm.name}, `;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate response based on message content
|
||||||
|
if (lowerMessage.includes("water") || lowerMessage.includes("irrigation")) {
|
||||||
|
return `${contextPrefix}I recommend watering deeply but infrequently to encourage strong root growth. Based on the current weather conditions${
|
||||||
|
farm ? ` in ${farm.location}` : ""
|
||||||
|
} (${farm?.weather?.rainfall || "minimal"} rainfall recently), you should water ${
|
||||||
|
crop ? `your ${crop.name}` : "your crops"
|
||||||
|
} approximately 2-3 times per week, ensuring the soil remains moist but not waterlogged.`;
|
||||||
|
} else if (lowerMessage.includes("fertiliz") || lowerMessage.includes("nutrient")) {
|
||||||
|
return `${contextPrefix}a balanced NPK fertilizer with a ratio of 10-10-10 would be suitable for general application. ${
|
||||||
|
crop
|
||||||
|
? `For ${crop.name} specifically, consider increasing ${
|
||||||
|
crop.name.toLowerCase().includes("tomato")
|
||||||
|
? "potassium"
|
||||||
|
: crop.name.toLowerCase().includes("corn")
|
||||||
|
? "nitrogen"
|
||||||
|
: "phosphorus"
|
||||||
|
} for optimal growth during the current ${
|
||||||
|
crop.progress && crop.progress < 30 ? "early" : crop.progress && crop.progress < 70 ? "middle" : "late"
|
||||||
|
} growth stage.`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
|
} else if (lowerMessage.includes("pest") || lowerMessage.includes("insect") || lowerMessage.includes("disease")) {
|
||||||
|
return `${contextPrefix}monitor for ${
|
||||||
|
crop
|
||||||
|
? crop.name.toLowerCase().includes("tomato")
|
||||||
|
? "tomato hornworms, aphids, and early blight"
|
||||||
|
: crop.name.toLowerCase().includes("corn")
|
||||||
|
? "corn borers, rootworms, and rust"
|
||||||
|
: "common agricultural pests"
|
||||||
|
: "common agricultural pests like aphids, beetles, and fungal diseases"
|
||||||
|
}. I recommend implementing integrated pest management (IPM) practices, including regular scouting, beneficial insects, and targeted treatments only when necessary.`;
|
||||||
|
} else if (lowerMessage.includes("harvest") || lowerMessage.includes("yield")) {
|
||||||
|
return `${contextPrefix}${
|
||||||
|
crop
|
||||||
|
? `your ${crop.name} should be ready to harvest in approximately ${Math.max(
|
||||||
|
1,
|
||||||
|
Math.round((100 - (crop.progress || 50)) / 10)
|
||||||
|
)} weeks based on the current growth stage. Look for ${
|
||||||
|
crop.name.toLowerCase().includes("tomato")
|
||||||
|
? "firm, fully colored fruits"
|
||||||
|
: crop.name.toLowerCase().includes("corn")
|
||||||
|
? "full ears with dried silk and plump kernels"
|
||||||
|
: "signs of maturity specific to this crop type"
|
||||||
|
}`
|
||||||
|
: "harvest timing depends on the specific crops you're growing, but generally you should look for visual cues of ripeness and maturity"
|
||||||
|
}.`;
|
||||||
|
} else if (lowerMessage.includes("soil") || lowerMessage.includes("compost")) {
|
||||||
|
return `${contextPrefix}improving soil health is crucial for sustainable farming. I recommend regular soil testing, adding organic matter through compost or cover crops, practicing crop rotation, and minimizing soil disturbance. ${
|
||||||
|
farm
|
||||||
|
? `Based on the soil type common in ${farm.location}, you might also consider adding ${
|
||||||
|
farm.location.includes("California") ? "gypsum to improve drainage" : "lime to adjust pH levels"
|
||||||
|
}.`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
|
} else if (lowerMessage.includes("weather") || lowerMessage.includes("forecast") || lowerMessage.includes("rain")) {
|
||||||
|
return `${contextPrefix}${
|
||||||
|
farm
|
||||||
|
? `the current conditions show temperature at ${farm.weather?.temperature}°C with ${farm.weather?.humidity}% humidity. There's been ${farm.weather?.rainfall} of rainfall recently, and sunlight levels are at ${farm.weather?.sunlight}% of optimal.`
|
||||||
|
: "I recommend checking your local agricultural weather service for the most accurate forecast for your specific location."
|
||||||
|
} ${
|
||||||
|
crop
|
||||||
|
? `For your ${crop.name}, ${
|
||||||
|
farm?.weather?.rainfall === "0mm"
|
||||||
|
? "the dry conditions mean you should increase irrigation"
|
||||||
|
: "the recent rainfall means you can reduce irrigation temporarily"
|
||||||
|
}.`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
|
} else {
|
||||||
|
return `${contextPrefix}I understand you're asking about "${message}". To provide the most helpful advice, could you provide more specific details about your farming goals or challenges? I'm here to help with crop management, pest control, irrigation strategies, and more.`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle selecting a farm
|
||||||
|
const handleFarmSelect = (farmId: string) => {
|
||||||
|
setSelectedFarm(farmId);
|
||||||
|
setSelectedCrop(null); // Reset crop selection when farm changes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle selecting a crop
|
||||||
|
const handleCropSelect = (cropId: string) => {
|
||||||
|
setSelectedCrop(cropId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle clicking a recommended prompt
|
||||||
|
const handlePromptClick = (promptText: string) => {
|
||||||
|
setInputValue(promptText);
|
||||||
|
handleSendMessage(promptText);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle loading a chat history item
|
||||||
|
const handleLoadChatHistory = (messageId: string) => {
|
||||||
|
// Find the message in history
|
||||||
|
const historyItem = mockChatHistory.find((msg) => msg.id === messageId);
|
||||||
|
if (!historyItem) return;
|
||||||
|
|
||||||
|
// Set related farm/crop if available
|
||||||
|
if (historyItem.relatedTo) {
|
||||||
|
if (historyItem.relatedTo.type === "farm") {
|
||||||
|
setSelectedFarm(historyItem.relatedTo.id);
|
||||||
|
setSelectedCrop(null);
|
||||||
|
} else if (historyItem.relatedTo.type === "crop") {
|
||||||
|
const crop = mockCrops.find((c) => c.id === historyItem.relatedTo?.id);
|
||||||
|
if (crop) {
|
||||||
|
setSelectedFarm(crop.farmId);
|
||||||
|
setSelectedCrop(historyItem.relatedTo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the conversation
|
||||||
|
const conversation = mockChatHistory.filter(
|
||||||
|
(msg) =>
|
||||||
|
msg.id === messageId ||
|
||||||
|
(msg.timestamp >= historyItem.timestamp && msg.timestamp <= new Date(historyItem.timestamp.getTime() + 60000))
|
||||||
|
);
|
||||||
|
|
||||||
|
setMessages(conversation);
|
||||||
|
setIsHistoryOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col min-h-screen bg-gradient-to-br from-green-50 to-blue-50 dark:from-green-950 dark:to-blue-950">
|
||||||
|
{/* Header */}
|
||||||
|
<header className="border-b bg-white dark:bg-gray-950 shadow-sm py-4 px-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button variant="ghost" size="icon" onClick={() => router.push("/farms")} aria-label="Back to farms">
|
||||||
|
<ChevronLeft className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<MessageSquare className="h-5 w-5 text-green-600 dark:text-green-400" />
|
||||||
|
<h1 className="text-xl font-semibold">ForFarm Assistant</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => setIsHistoryOpen(!isHistoryOpen)}
|
||||||
|
aria-label={isHistoryOpen ? "Close history" : "Open history"}>
|
||||||
|
{isHistoryOpen ? <PanelRightClose className="h-5 w-5" /> : <PanelRightOpen className="h-5 w-5" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
{/* Chat area */}
|
||||||
|
<div className="flex-1 flex flex-col h-full overflow-hidden">
|
||||||
|
{/* Farm/Crop selector */}
|
||||||
|
<div className="bg-white dark:bg-gray-900 p-4 border-b">
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="text-sm font-medium mb-1 block text-gray-700 dark:text-gray-300">
|
||||||
|
Select Farm (Optional)
|
||||||
|
</label>
|
||||||
|
<Select value={selectedFarm || ""} onValueChange={handleFarmSelect}>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="All Farms" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All Farms</SelectItem>
|
||||||
|
{mockFarms.map((farm) => (
|
||||||
|
<SelectItem key={farm.id} value={farm.id}>
|
||||||
|
{farm.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<label className="text-sm font-medium mb-1 block text-gray-700 dark:text-gray-300">
|
||||||
|
Select Crop (Optional)
|
||||||
|
</label>
|
||||||
|
<Select value={selectedCrop || ""} onValueChange={handleCropSelect} disabled={!selectedFarm}>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder={selectedFarm ? "All Crops" : "Select a farm first"} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{selectedFarm && <SelectItem value="all">All Crops</SelectItem>}
|
||||||
|
{filteredCrops.map((crop) => (
|
||||||
|
<SelectItem key={crop.id} value={crop.id}>
|
||||||
|
{crop.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Messages */}
|
||||||
|
<ScrollArea className="flex-1 p-4">
|
||||||
|
<div className="space-y-4 max-w-3xl mx-auto">
|
||||||
|
{messages.map((message) => (
|
||||||
|
<div key={message.id} className={`flex ${message.sender === "user" ? "justify-end" : "justify-start"}`}>
|
||||||
|
<div
|
||||||
|
className={`max-w-[80%] rounded-lg p-4 ${
|
||||||
|
message.sender === "user"
|
||||||
|
? "bg-green-600 text-white dark:bg-green-700"
|
||||||
|
: "bg-white dark:bg-gray-800 border dark:border-gray-700 shadow-sm"
|
||||||
|
}`}>
|
||||||
|
{message.relatedTo && (
|
||||||
|
<div className="mb-1">
|
||||||
|
<Badge variant="outline" className="text-xs font-normal">
|
||||||
|
{message.relatedTo.type === "farm" ? "🏡 " : "🌱 "}
|
||||||
|
{message.relatedTo.name}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-sm">{message.content}</div>
|
||||||
|
<div className="mt-1 text-xs opacity-70 text-right">
|
||||||
|
{message.timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex justify-start">
|
||||||
|
<div className="max-w-[80%] rounded-lg p-4 bg-white dark:bg-gray-800 border dark:border-gray-700 shadow-sm">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="h-2 w-2 bg-green-500 rounded-full animate-pulse"></div>
|
||||||
|
<div className="h-2 w-2 bg-green-500 rounded-full animate-pulse delay-150"></div>
|
||||||
|
<div className="h-2 w-2 bg-green-500 rounded-full animate-pulse delay-300"></div>
|
||||||
|
<span className="text-sm text-gray-500 dark:text-gray-400 ml-1">
|
||||||
|
ForFarm Assistant is typing...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
|
{/* Recommended prompts */}
|
||||||
|
<div className="bg-white dark:bg-gray-900 border-t p-4">
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center gap-1">
|
||||||
|
<Sparkles className="h-4 w-4 text-green-500" />
|
||||||
|
Recommended Questions
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{recommendedPrompts.map((prompt) => (
|
||||||
|
<Button
|
||||||
|
key={prompt.id}
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="text-xs rounded-full bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800 hover:bg-green-100 dark:hover:bg-green-900/40 text-green-800 dark:text-green-300"
|
||||||
|
onClick={() => handlePromptClick(prompt.text)}>
|
||||||
|
{prompt.text}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input area */}
|
||||||
|
<div className="bg-white dark:bg-gray-900 border-t p-4">
|
||||||
|
<div className="flex gap-2 max-w-3xl mx-auto">
|
||||||
|
<Input
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
placeholder="Ask about your farm or crops..."
|
||||||
|
className="flex-1"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSendMessage();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleSendMessage()}
|
||||||
|
disabled={!inputValue.trim() || isLoading}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white">
|
||||||
|
<Send className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Chat history sidebar */}
|
||||||
|
{isHistoryOpen && (
|
||||||
|
<div className="w-80 border-l bg-white dark:bg-gray-900 flex flex-col">
|
||||||
|
<div className="p-4 border-b flex items-center justify-between">
|
||||||
|
<h2 className="font-medium flex items-center gap-1">
|
||||||
|
<History className="h-4 w-4" />
|
||||||
|
Chat History
|
||||||
|
</h2>
|
||||||
|
<Button variant="ghost" size="icon" onClick={() => setIsHistoryOpen(false)}>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-3">
|
||||||
|
<div className="relative">
|
||||||
|
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500 dark:text-gray-400" />
|
||||||
|
<Input placeholder="Search conversations..." className="pl-9" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tabs defaultValue="recent" className="flex-1 flex flex-col">
|
||||||
|
<TabsList className="mx-3 mb-2">
|
||||||
|
<TabsTrigger value="recent" className="flex-1">
|
||||||
|
Recent
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="farms" className="flex-1">
|
||||||
|
By Farm
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="crops" className="flex-1">
|
||||||
|
By Crop
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<ScrollArea className="flex-1">
|
||||||
|
<TabsContent value="recent" className="m-0 p-0">
|
||||||
|
<div className="space-y-1 p-2">
|
||||||
|
{mockChatHistory
|
||||||
|
.filter((msg) => msg.sender === "user")
|
||||||
|
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
|
||||||
|
.map((message) => (
|
||||||
|
<Card
|
||||||
|
key={message.id}
|
||||||
|
className="p-3 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
||||||
|
onClick={() => handleLoadChatHistory(message.id)}>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<Avatar className="h-8 w-8 bg-green-100 dark:bg-green-900">
|
||||||
|
<div className="text-xs font-medium text-green-700 dark:text-green-300">
|
||||||
|
{message.relatedTo?.name.substring(0, 2) || "Me"}
|
||||||
|
</div>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<p className="text-sm font-medium truncate">
|
||||||
|
{message.relatedTo ? message.relatedTo.name : "General Question"}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1">
|
||||||
|
<Clock className="h-3 w-3" />
|
||||||
|
{message.timestamp.toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-600 dark:text-gray-300 truncate mt-1">
|
||||||
|
{message.content}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="farms" className="m-0 p-0">
|
||||||
|
<div className="space-y-3 p-3">
|
||||||
|
{mockFarms.map((farm) => (
|
||||||
|
<div key={farm.id}>
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">{farm.name}</h3>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{mockChatHistory
|
||||||
|
.filter(
|
||||||
|
(msg) =>
|
||||||
|
msg.sender === "user" && msg.relatedTo?.type === "farm" && msg.relatedTo.id === farm.id
|
||||||
|
)
|
||||||
|
.map((message) => (
|
||||||
|
<Card
|
||||||
|
key={message.id}
|
||||||
|
className="p-3 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
||||||
|
onClick={() => handleLoadChatHistory(message.id)}>
|
||||||
|
<p className="text-xs text-gray-600 dark:text-gray-300 truncate">{message.content}</p>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||||
|
{message.timestamp.toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Separator className="my-3" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="crops" className="m-0 p-0">
|
||||||
|
<div className="space-y-3 p-3">
|
||||||
|
{mockCrops.map((crop) => (
|
||||||
|
<div key={crop.id}>
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 flex items-center gap-1">
|
||||||
|
<Leaf className="h-3 w-3 text-green-600" />
|
||||||
|
{crop.name}
|
||||||
|
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
({mockFarms.find((f) => f.id === crop.farmId)?.name})
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{mockChatHistory
|
||||||
|
.filter(
|
||||||
|
(msg) =>
|
||||||
|
msg.sender === "user" && msg.relatedTo?.type === "crop" && msg.relatedTo.id === crop.id
|
||||||
|
)
|
||||||
|
.map((message) => (
|
||||||
|
<Card
|
||||||
|
key={message.id}
|
||||||
|
className="p-3 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
||||||
|
onClick={() => handleLoadChatHistory(message.id)}>
|
||||||
|
<p className="text-xs text-gray-600 dark:text-gray-300 truncate">{message.content}</p>
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||||
|
{message.timestamp.toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Separator className="my-3" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</ScrollArea>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
99
frontend/app/(sidebar)/marketplace/loading.tsx
Normal file
99
frontend/app/(sidebar)/marketplace/loading.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
|
import { RefreshCw } from "lucide-react";
|
||||||
|
|
||||||
|
export default function MarketplaceLoading() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto py-6 px-4 md:px-6">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4">
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-10 w-64 mb-2" />
|
||||||
|
<Skeleton className="h-5 w-96" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Skeleton className="h-9 w-32" />
|
||||||
|
<Skeleton className="h-5 w-40" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
||||||
|
<Card className="md:col-span-3">
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between md:items-center gap-4">
|
||||||
|
<div>
|
||||||
|
<Skeleton className="h-6 w-40 mb-2" />
|
||||||
|
<Skeleton className="h-4 w-60" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-2">
|
||||||
|
<Skeleton className="h-10 w-[180px]" />
|
||||||
|
<Skeleton className="h-10 w-[140px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="w-full h-[300px] flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<RefreshCw className="h-8 w-8 animate-spin mx-auto text-primary/70" />
|
||||||
|
<p className="mt-2 text-sm text-muted-foreground">Loading market data...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
|
<Skeleton className="h-24 w-full" />
|
||||||
|
<Skeleton className="h-24 w-full" />
|
||||||
|
<Skeleton className="h-24 w-full" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<Skeleton className="h-6 w-40 mb-2" />
|
||||||
|
<Skeleton className="h-4 w-48" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
<Skeleton className="h-4 w-3/4" />
|
||||||
|
<Skeleton className="h-4 w-5/6" />
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
<Skeleton className="h-4 w-3/4" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<Skeleton className="h-6 w-40 mb-2" />
|
||||||
|
<Skeleton className="h-4 w-48" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Skeleton className="h-16 w-full rounded-lg" />
|
||||||
|
<Skeleton className="h-16 w-full rounded-lg" />
|
||||||
|
<Skeleton className="h-16 w-full rounded-lg" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<Skeleton className="h-6 w-40 mb-2" />
|
||||||
|
<Skeleton className="h-4 w-80" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Skeleton className="h-8 w-full" />
|
||||||
|
<Skeleton className="h-8 w-full" />
|
||||||
|
<Skeleton className="h-8 w-full" />
|
||||||
|
<Skeleton className="h-8 w-full" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
1020
frontend/app/(sidebar)/marketplace/page.tsx
Normal file
1020
frontend/app/(sidebar)/marketplace/page.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -39,22 +39,22 @@ export default function Home() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</Link>
|
</Link>
|
||||||
<nav className="hidden md:flex space-x-6 items-center">
|
<nav className="hidden md:flex space-x-6 items-center">
|
||||||
<Link href="/features" className="hover:text-green-200 transition-colors">
|
<Link href="/hub" className="hover:text-green-200 transition-colors">
|
||||||
Features
|
Features
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/pricing" className="hover:text-green-200 transition-colors">
|
{/* <Link href="/pricing" className="hover:text-green-200 transition-colors">
|
||||||
Pricing
|
Pricing
|
||||||
</Link>
|
</Link> */}
|
||||||
<Link href="/knowledge-hub" className="hover:text-green-200 transition-colors">
|
<Link href="/hub" className="hover:text-green-200 transition-colors">
|
||||||
Knowledge Hub
|
Knowledge Hub
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/documentation" className="hover:text-green-200 transition-colors">
|
{/* <Link href="/documentation" className="hover:text-green-200 transition-colors">
|
||||||
Documentation
|
Documentation
|
||||||
</Link>
|
</Link> */}
|
||||||
</nav>
|
</nav>
|
||||||
<div className="flex space-x-3 items-center">
|
<div className="flex space-x-3 items-center">
|
||||||
<Link
|
<Link
|
||||||
href="/auth/login"
|
href="/auth/signin"
|
||||||
className="hidden md:inline-block hover:text-green-200 transition-colors font-medium">
|
className="hidden md:inline-block hover:text-green-200 transition-colors font-medium">
|
||||||
Log in
|
Log in
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -44,6 +44,7 @@
|
|||||||
"react-day-picker": "8.10.1",
|
"react-day-picker": "8.10.1",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
"recharts": "^2.15.1",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
@ -113,6 +113,9 @@ importers:
|
|||||||
react-hook-form:
|
react-hook-form:
|
||||||
specifier: ^7.54.2
|
specifier: ^7.54.2
|
||||||
version: 7.54.2(react@19.0.0)
|
version: 7.54.2(react@19.0.0)
|
||||||
|
recharts:
|
||||||
|
specifier: ^2.15.1
|
||||||
|
version: 2.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
@ -953,6 +956,33 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19
|
react: ^18 || ^19
|
||||||
|
|
||||||
|
'@types/d3-array@3.2.1':
|
||||||
|
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||||
|
|
||||||
|
'@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.6':
|
'@types/estree@1.0.6':
|
||||||
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
||||||
|
|
||||||
@ -1233,6 +1263,50 @@ packages:
|
|||||||
csstype@3.1.3:
|
csstype@3.1.3:
|
||||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||||
|
|
||||||
|
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:
|
damerau-levenshtein@1.0.8:
|
||||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||||
|
|
||||||
@ -1268,6 +1342,9 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decimal.js-light@2.5.1:
|
||||||
|
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
|
||||||
|
|
||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
@ -1300,6 +1377,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
dom-helpers@5.2.1:
|
||||||
|
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -1469,9 +1549,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
eventemitter3@4.0.7:
|
||||||
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3:
|
fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
|
fast-equals@5.2.2:
|
||||||
|
resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
fast-glob@3.3.1:
|
fast-glob@3.3.1:
|
||||||
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
@ -1650,6 +1737,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
internmap@2.0.3:
|
||||||
|
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
invariant@2.2.4:
|
invariant@2.2.4:
|
||||||
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
||||||
|
|
||||||
@ -1848,6 +1939,9 @@ packages:
|
|||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
|
lodash@4.17.21:
|
||||||
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
|
|
||||||
loose-envify@1.4.0:
|
loose-envify@1.4.0:
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -2167,6 +2261,9 @@ packages:
|
|||||||
react-is@16.13.1:
|
react-is@16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
|
|
||||||
|
react-is@18.3.1:
|
||||||
|
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||||
|
|
||||||
react-remove-scroll-bar@2.3.8:
|
react-remove-scroll-bar@2.3.8:
|
||||||
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -2187,6 +2284,12 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-smooth@4.0.4:
|
||||||
|
resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
react-style-singleton@2.2.3:
|
react-style-singleton@2.2.3:
|
||||||
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -2197,6 +2300,12 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-transition-group@4.4.5:
|
||||||
|
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.6.0'
|
||||||
|
react-dom: '>=16.6.0'
|
||||||
|
|
||||||
react@19.0.0:
|
react@19.0.0:
|
||||||
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
|
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -2208,6 +2317,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
engines: {node: '>=8.10.0'}
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
|
recharts-scale@0.4.5:
|
||||||
|
resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==}
|
||||||
|
|
||||||
|
recharts@2.15.1:
|
||||||
|
resolution: {integrity: sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
reflect.getprototypeof@1.0.10:
|
reflect.getprototypeof@1.0.10:
|
||||||
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -2424,6 +2543,9 @@ packages:
|
|||||||
thenify@3.3.1:
|
thenify@3.3.1:
|
||||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||||
|
|
||||||
|
tiny-invariant@1.3.3:
|
||||||
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
@ -2505,6 +2627,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
victory-vendor@36.9.2:
|
||||||
|
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -3323,6 +3448,30 @@ snapshots:
|
|||||||
'@tanstack/query-core': 5.66.0
|
'@tanstack/query-core': 5.66.0
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
|
'@types/d3-array@3.2.1': {}
|
||||||
|
|
||||||
|
'@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.6': {}
|
'@types/estree@1.0.6': {}
|
||||||
|
|
||||||
'@types/google.maps@3.58.1': {}
|
'@types/google.maps@3.58.1': {}
|
||||||
@ -3653,6 +3802,44 @@ snapshots:
|
|||||||
|
|
||||||
csstype@3.1.3: {}
|
csstype@3.1.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: {}
|
damerau-levenshtein@1.0.8: {}
|
||||||
|
|
||||||
data-view-buffer@1.0.2:
|
data-view-buffer@1.0.2:
|
||||||
@ -3683,6 +3870,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decimal.js-light@2.5.1: {}
|
||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
define-data-property@1.1.4:
|
define-data-property@1.1.4:
|
||||||
@ -3712,6 +3901,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
|
|
||||||
|
dom-helpers@5.2.1:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.7
|
||||||
|
csstype: 3.1.3
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.1
|
call-bind-apply-helpers: 1.0.1
|
||||||
@ -4026,8 +4220,12 @@ snapshots:
|
|||||||
|
|
||||||
esutils@2.0.3: {}
|
esutils@2.0.3: {}
|
||||||
|
|
||||||
|
eventemitter3@4.0.7: {}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
|
|
||||||
|
fast-equals@5.2.2: {}
|
||||||
|
|
||||||
fast-glob@3.3.1:
|
fast-glob@3.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@ -4211,6 +4409,8 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
side-channel: 1.1.0
|
side-channel: 1.1.0
|
||||||
|
|
||||||
|
internmap@2.0.3: {}
|
||||||
|
|
||||||
invariant@2.2.4:
|
invariant@2.2.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
@ -4412,6 +4612,8 @@ snapshots:
|
|||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
|
lodash@4.17.21: {}
|
||||||
|
|
||||||
loose-envify@1.4.0:
|
loose-envify@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
@ -4711,6 +4913,8 @@ snapshots:
|
|||||||
|
|
||||||
react-is@16.13.1: {}
|
react-is@16.13.1: {}
|
||||||
|
|
||||||
|
react-is@18.3.1: {}
|
||||||
|
|
||||||
react-remove-scroll-bar@2.3.8(@types/react@19.0.8)(react@19.0.0):
|
react-remove-scroll-bar@2.3.8(@types/react@19.0.8)(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
@ -4730,6 +4934,14 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.0.8
|
'@types/react': 19.0.8
|
||||||
|
|
||||||
|
react-smooth@4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
fast-equals: 5.2.2
|
||||||
|
prop-types: 15.8.1
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
|
||||||
react-style-singleton@2.2.3(@types/react@19.0.8)(react@19.0.0):
|
react-style-singleton@2.2.3(@types/react@19.0.8)(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
get-nonce: 1.0.1
|
get-nonce: 1.0.1
|
||||||
@ -4738,6 +4950,15 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.0.8
|
'@types/react': 19.0.8
|
||||||
|
|
||||||
|
react-transition-group@4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.7
|
||||||
|
dom-helpers: 5.2.1
|
||||||
|
loose-envify: 1.4.0
|
||||||
|
prop-types: 15.8.1
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
|
||||||
react@19.0.0: {}
|
react@19.0.0: {}
|
||||||
|
|
||||||
read-cache@1.0.0:
|
read-cache@1.0.0:
|
||||||
@ -4748,6 +4969,23 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
recharts-scale@0.4.5:
|
||||||
|
dependencies:
|
||||||
|
decimal.js-light: 2.5.1
|
||||||
|
|
||||||
|
recharts@2.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
clsx: 2.1.1
|
||||||
|
eventemitter3: 4.0.7
|
||||||
|
lodash: 4.17.21
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
react-is: 18.3.1
|
||||||
|
react-smooth: 4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
recharts-scale: 0.4.5
|
||||||
|
tiny-invariant: 1.3.3
|
||||||
|
victory-vendor: 36.9.2
|
||||||
|
|
||||||
reflect.getprototypeof@1.0.10:
|
reflect.getprototypeof@1.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.8
|
call-bind: 1.0.8
|
||||||
@ -5055,6 +5293,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
|
|
||||||
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
@ -5145,6 +5385,23 @@ snapshots:
|
|||||||
|
|
||||||
uuid@8.3.2: {}
|
uuid@8.3.2: {}
|
||||||
|
|
||||||
|
victory-vendor@36.9.2:
|
||||||
|
dependencies:
|
||||||
|
'@types/d3-array': 3.2.1
|
||||||
|
'@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:
|
which-boxed-primitive@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-bigint: 1.1.0
|
is-bigint: 1.1.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user