Spaces:
Build error
Build error
Upload 32 files
Browse files- .gitattributes +2 -0
- AboutPage.tsx +202 -0
- App.tsx +36 -0
- Footer.tsx +143 -0
- HomePage.tsx +103 -0
- LocationsPage.tsx +282 -0
- Navbar.tsx +96 -0
- PricingPage.tsx +239 -0
- README.md +54 -13
- VehicleCard.tsx +76 -0
- VehicleDetailPage.tsx +327 -0
- VehiclesPage.tsx +157 -0
- bun.lock +546 -0
- eslint.config.js +28 -0
- index.css +97 -0
- index.html +13 -0
- main.tsx +10 -0
- package.json +35 -0
- postcss.config.js +6 -0
- react.svg +1 -0
- tailwind.config.js +11 -0
- tesla-cybertruck.png +1 -0
- tesla-model-3.png +0 -0
- tesla-model-s.png +0 -0
- tesla-model-x.png +3 -0
- tesla-model-y.png +3 -0
- tsconfig.app.json +26 -0
- tsconfig.json +7 -0
- tsconfig.node.json +24 -0
- vehicles.ts +162 -0
- vite-env.d.ts +1 -0
- vite.config.ts +17 -0
- vite.svg +1 -0
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
tesla-model-x.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
tesla-model-y.png filter=lfs diff=lfs merge=lfs -text
|
AboutPage.tsx
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Link } from 'react-router-dom';
|
2 |
+
import { ChevronRight } from 'lucide-react';
|
3 |
+
|
4 |
+
const teamMembers = [
|
5 |
+
{
|
6 |
+
name: 'Alexis Chen',
|
7 |
+
role: 'Founder & CEO',
|
8 |
+
bio: 'Former Tesla executive with over 15 years of experience in the electric vehicle industry. Passionate about sustainable transportation and innovative mobility solutions.',
|
9 |
+
image: 'https://images.unsplash.com/photo-1564564321837-a57b7070ac4f?q=80&w=200&h=200&auto=format&fit=crop',
|
10 |
+
},
|
11 |
+
{
|
12 |
+
name: 'Marcus Johnson',
|
13 |
+
role: 'CTO',
|
14 |
+
bio: 'Tech innovator with background in automotive software and AI. Led development of several EV charging networks before joining CarFleet to revolutionize electric vehicle rentals.',
|
15 |
+
image: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?q=80&w=200&h=200&auto=format&fit=crop',
|
16 |
+
},
|
17 |
+
{
|
18 |
+
name: 'Sophia Rodriguez',
|
19 |
+
role: 'COO',
|
20 |
+
bio: 'Operations expert who previously scaled multiple tech startups. Oversees our network of locations and ensures every rental experience exceeds customer expectations.',
|
21 |
+
image: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=200&h=200&auto=format&fit=crop',
|
22 |
+
},
|
23 |
+
{
|
24 |
+
name: 'David Kim',
|
25 |
+
role: 'Head of Customer Experience',
|
26 |
+
bio: 'Hospitality industry veteran focused on creating premium experiences. Designs our customer journey from booking to return with a focus on convenience and satisfaction.',
|
27 |
+
image: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=200&h=200&auto=format&fit=crop',
|
28 |
+
},
|
29 |
+
];
|
30 |
+
|
31 |
+
const timeline = [
|
32 |
+
{
|
33 |
+
year: '2021',
|
34 |
+
title: 'Company Founded',
|
35 |
+
description: 'CarFleet was founded with a vision to accelerate the adoption of electric vehicles through flexible rental and subscription options.',
|
36 |
+
},
|
37 |
+
{
|
38 |
+
year: '2022',
|
39 |
+
title: 'First Location Launch',
|
40 |
+
description: 'Opened our first rental location in San Francisco with a fleet of 20 Tesla vehicles.',
|
41 |
+
},
|
42 |
+
{
|
43 |
+
year: '2023',
|
44 |
+
title: 'Expansion to Major Cities',
|
45 |
+
description: 'Expanded to Los Angeles, New York, Miami, Chicago, and Seattle with an increased fleet of 200+ vehicles.',
|
46 |
+
},
|
47 |
+
{
|
48 |
+
year: '2024',
|
49 |
+
title: 'Introduction of Premium Subscription Plans',
|
50 |
+
description: 'Launched our tiered subscription plans to provide flexible options for different usage needs.',
|
51 |
+
},
|
52 |
+
{
|
53 |
+
year: '2025',
|
54 |
+
title: 'Expansion to Additional EV Brands',
|
55 |
+
description: 'Added other premium electric vehicle brands to our fleet while maintaining our core Tesla rental business.',
|
56 |
+
},
|
57 |
+
];
|
58 |
+
|
59 |
+
const AboutPage = () => {
|
60 |
+
return (
|
61 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
62 |
+
{/* Hero Section */}
|
63 |
+
<section className="relative py-20 overflow-hidden">
|
64 |
+
<div className="absolute inset-0 bg-gradient-to-r from-blue-900/20 to-purple-900/20 z-0"></div>
|
65 |
+
<div className="container mx-auto px-4 relative z-10">
|
66 |
+
<div className="max-w-3xl mx-auto text-center">
|
67 |
+
<h1 className="text-4xl md:text-5xl font-bold mb-6">About CarFleet</h1>
|
68 |
+
<p className="text-gray-300 text-lg md:text-xl">
|
69 |
+
We're on a mission to accelerate the world's transition to sustainable transportation by making electric vehicles accessible to everyone.
|
70 |
+
</p>
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
</section>
|
74 |
+
|
75 |
+
{/* Mission Section */}
|
76 |
+
<section className="py-16 bg-[#0c0c0c]">
|
77 |
+
<div className="container mx-auto px-4">
|
78 |
+
<div className="max-w-3xl mx-auto">
|
79 |
+
<h2 className="text-3xl font-bold mb-6">Our Mission</h2>
|
80 |
+
<p className="text-gray-300 text-lg mb-6">
|
81 |
+
At CarFleet, we believe that the future of transportation is electric. Our mission is to accelerate the adoption of sustainable transportation by providing accessible electric vehicle rental and subscription options.
|
82 |
+
</p>
|
83 |
+
<p className="text-gray-300 text-lg mb-6">
|
84 |
+
We're committed to reducing carbon emissions and fighting climate change by helping more people experience the benefits of electric vehicles without the commitment of ownership.
|
85 |
+
</p>
|
86 |
+
<p className="text-gray-300 text-lg">
|
87 |
+
Through our flexible rental plans, premium customer service, and growing network of charging stations, we're making electric mobility convenient and enjoyable for everyone.
|
88 |
+
</p>
|
89 |
+
</div>
|
90 |
+
</div>
|
91 |
+
</section>
|
92 |
+
|
93 |
+
{/* Company Timeline */}
|
94 |
+
<section className="py-16">
|
95 |
+
<div className="container mx-auto px-4">
|
96 |
+
<div className="max-w-3xl mx-auto">
|
97 |
+
<h2 className="text-3xl font-bold mb-10 text-center">Our Journey</h2>
|
98 |
+
<div className="relative border-l border-gray-800 ml-6">
|
99 |
+
{timeline.map((item, index) => (
|
100 |
+
<div key={index} className="mb-10 ml-6">
|
101 |
+
<div className="absolute w-4 h-4 bg-blue-600 rounded-full -left-[10px] mt-1.5"></div>
|
102 |
+
<span className="absolute -left-[4.5rem] bg-[#161617] px-2 py-1 rounded-md text-sm font-semibold">
|
103 |
+
{item.year}
|
104 |
+
</span>
|
105 |
+
<h3 className="text-xl font-semibold mb-1">{item.title}</h3>
|
106 |
+
<p className="text-gray-400">{item.description}</p>
|
107 |
+
</div>
|
108 |
+
))}
|
109 |
+
</div>
|
110 |
+
</div>
|
111 |
+
</div>
|
112 |
+
</section>
|
113 |
+
|
114 |
+
{/* Team Section */}
|
115 |
+
<section className="py-16 bg-[#0c0c0c]">
|
116 |
+
<div className="container mx-auto px-4">
|
117 |
+
<div className="max-w-5xl mx-auto">
|
118 |
+
<h2 className="text-3xl font-bold mb-12 text-center">Our Team</h2>
|
119 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
120 |
+
{teamMembers.map((member, index) => (
|
121 |
+
<div key={index} className="bg-[#161617] rounded-lg overflow-hidden">
|
122 |
+
<img
|
123 |
+
src={member.image}
|
124 |
+
alt={member.name}
|
125 |
+
className="w-full h-64 object-cover"
|
126 |
+
/>
|
127 |
+
<div className="p-5">
|
128 |
+
<h3 className="text-lg font-semibold">{member.name}</h3>
|
129 |
+
<p className="text-blue-500 text-sm mb-3">{member.role}</p>
|
130 |
+
<p className="text-gray-400 text-sm line-clamp-4">{member.bio}</p>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
+
))}
|
134 |
+
</div>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
</section>
|
138 |
+
|
139 |
+
{/* Values Section */}
|
140 |
+
<section className="py-16">
|
141 |
+
<div className="container mx-auto px-4">
|
142 |
+
<div className="max-w-5xl mx-auto text-center mb-12">
|
143 |
+
<h2 className="text-3xl font-bold mb-6">Our Values</h2>
|
144 |
+
<p className="text-gray-300 text-lg max-w-3xl mx-auto">
|
145 |
+
These core principles guide everything we do, from how we treat our customers to how we develop our services.
|
146 |
+
</p>
|
147 |
+
</div>
|
148 |
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
149 |
+
<div className="bg-[#161617] p-6 rounded-lg">
|
150 |
+
<h3 className="text-xl font-semibold mb-3">Sustainability</h3>
|
151 |
+
<p className="text-gray-400">
|
152 |
+
We're committed to reducing carbon emissions and promoting sustainable transportation. Our all-electric fleet helps customers reduce their environmental footprint.
|
153 |
+
</p>
|
154 |
+
</div>
|
155 |
+
<div className="bg-[#161617] p-6 rounded-lg">
|
156 |
+
<h3 className="text-xl font-semibold mb-3">Innovation</h3>
|
157 |
+
<p className="text-gray-400">
|
158 |
+
We embrace cutting-edge technology and continuously improve our services. From our app to our vehicle features, we're always looking for ways to enhance the customer experience.
|
159 |
+
</p>
|
160 |
+
</div>
|
161 |
+
<div className="bg-[#161617] p-6 rounded-lg">
|
162 |
+
<h3 className="text-xl font-semibold mb-3">Accessibility</h3>
|
163 |
+
<p className="text-gray-400">
|
164 |
+
We believe everyone should have access to premium electric vehicles. Our flexible rental and subscription options make it possible for more people to experience the future of transportation.
|
165 |
+
</p>
|
166 |
+
</div>
|
167 |
+
</div>
|
168 |
+
</div>
|
169 |
+
</section>
|
170 |
+
|
171 |
+
{/* CTA Section */}
|
172 |
+
<section className="py-16 bg-[#0c0c0c]">
|
173 |
+
<div className="container mx-auto px-4">
|
174 |
+
<div className="max-w-3xl mx-auto text-center">
|
175 |
+
<h2 className="text-3xl font-bold mb-6">Join Our Vision</h2>
|
176 |
+
<p className="text-gray-300 text-lg mb-8">
|
177 |
+
Experience the future of transportation today. Rent a Tesla or subscribe to one of our premium plans.
|
178 |
+
</p>
|
179 |
+
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
180 |
+
<Link
|
181 |
+
to="/vehicles"
|
182 |
+
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-md font-medium transition-colors flex items-center justify-center gap-2"
|
183 |
+
>
|
184 |
+
Browse Electric Vehicles
|
185 |
+
<ChevronRight size={16} />
|
186 |
+
</Link>
|
187 |
+
<Link
|
188 |
+
to="/pricing"
|
189 |
+
className="bg-transparent border border-blue-600 text-blue-600 hover:bg-blue-600/10 px-6 py-3 rounded-md font-medium transition-colors flex items-center justify-center gap-2"
|
190 |
+
>
|
191 |
+
View Subscription Plans
|
192 |
+
<ChevronRight size={16} />
|
193 |
+
</Link>
|
194 |
+
</div>
|
195 |
+
</div>
|
196 |
+
</div>
|
197 |
+
</section>
|
198 |
+
</div>
|
199 |
+
);
|
200 |
+
};
|
201 |
+
|
202 |
+
export default AboutPage;
|
App.tsx
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
2 |
+
|
3 |
+
// Layout
|
4 |
+
import Navbar from '@/components/layout/Navbar';
|
5 |
+
import Footer from '@/components/layout/Footer';
|
6 |
+
|
7 |
+
// Pages
|
8 |
+
import HomePage from '@/pages/HomePage';
|
9 |
+
import VehiclesPage from '@/pages/VehiclesPage';
|
10 |
+
import VehicleDetailPage from '@/pages/VehicleDetailPage';
|
11 |
+
import PricingPage from '@/pages/PricingPage';
|
12 |
+
import LocationsPage from '@/pages/LocationsPage';
|
13 |
+
import AboutPage from '@/pages/AboutPage';
|
14 |
+
|
15 |
+
function App() {
|
16 |
+
return (
|
17 |
+
<Router>
|
18 |
+
<div className="flex flex-col min-h-screen bg-[#0a0a0a]">
|
19 |
+
<Navbar />
|
20 |
+
<main className="flex-grow">
|
21 |
+
<Routes>
|
22 |
+
<Route path="/" element={<HomePage />} />
|
23 |
+
<Route path="/vehicles" element={<VehiclesPage />} />
|
24 |
+
<Route path="/vehicles/:id" element={<VehicleDetailPage />} />
|
25 |
+
<Route path="/pricing" element={<PricingPage />} />
|
26 |
+
<Route path="/locations" element={<LocationsPage />} />
|
27 |
+
<Route path="/about" element={<AboutPage />} />
|
28 |
+
</Routes>
|
29 |
+
</main>
|
30 |
+
<Footer />
|
31 |
+
</div>
|
32 |
+
</Router>
|
33 |
+
);
|
34 |
+
}
|
35 |
+
|
36 |
+
export default App;
|
Footer.tsx
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Link } from 'react-router-dom';
|
2 |
+
import { Facebook, Twitter, Instagram, Youtube } from 'lucide-react';
|
3 |
+
|
4 |
+
const Footer = () => {
|
5 |
+
return (
|
6 |
+
<footer className="bg-[#0f0f0f] border-t border-gray-800">
|
7 |
+
<div className="container mx-auto px-4 py-12">
|
8 |
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
9 |
+
<div>
|
10 |
+
<Link to="/" className="flex items-center space-x-2">
|
11 |
+
<span className="text-blue-500 text-xl font-bold">CarFleet</span>
|
12 |
+
</Link>
|
13 |
+
<p className="mt-4 text-gray-400 text-sm">
|
14 |
+
Premium electric vehicle rental and subscription service for the modern driver.
|
15 |
+
</p>
|
16 |
+
<div className="mt-6 flex space-x-4">
|
17 |
+
<a
|
18 |
+
href="#"
|
19 |
+
className="text-gray-400 hover:text-white transition-colors"
|
20 |
+
aria-label="Facebook"
|
21 |
+
>
|
22 |
+
<Facebook size={20} />
|
23 |
+
</a>
|
24 |
+
<a
|
25 |
+
href="#"
|
26 |
+
className="text-gray-400 hover:text-white transition-colors"
|
27 |
+
aria-label="Twitter"
|
28 |
+
>
|
29 |
+
<Twitter size={20} />
|
30 |
+
</a>
|
31 |
+
<a
|
32 |
+
href="#"
|
33 |
+
className="text-gray-400 hover:text-white transition-colors"
|
34 |
+
aria-label="Instagram"
|
35 |
+
>
|
36 |
+
<Instagram size={20} />
|
37 |
+
</a>
|
38 |
+
<a
|
39 |
+
href="#"
|
40 |
+
className="text-gray-400 hover:text-white transition-colors"
|
41 |
+
aria-label="Youtube"
|
42 |
+
>
|
43 |
+
<Youtube size={20} />
|
44 |
+
</a>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
|
48 |
+
<div>
|
49 |
+
<h3 className="text-white font-medium mb-4">Quick Links</h3>
|
50 |
+
<ul className="space-y-2">
|
51 |
+
<li>
|
52 |
+
<Link to="/" className="text-gray-400 hover:text-white transition-colors text-sm">
|
53 |
+
Home
|
54 |
+
</Link>
|
55 |
+
</li>
|
56 |
+
<li>
|
57 |
+
<Link to="/about" className="text-gray-400 hover:text-white transition-colors text-sm">
|
58 |
+
About Us
|
59 |
+
</Link>
|
60 |
+
</li>
|
61 |
+
<li>
|
62 |
+
<Link to="/vehicles" className="text-gray-400 hover:text-white transition-colors text-sm">
|
63 |
+
Our Fleet
|
64 |
+
</Link>
|
65 |
+
</li>
|
66 |
+
<li>
|
67 |
+
<Link to="/pricing" className="text-gray-400 hover:text-white transition-colors text-sm">
|
68 |
+
Pricing
|
69 |
+
</Link>
|
70 |
+
</li>
|
71 |
+
<li>
|
72 |
+
<Link to="/contact" className="text-gray-400 hover:text-white transition-colors text-sm">
|
73 |
+
Contact
|
74 |
+
</Link>
|
75 |
+
</li>
|
76 |
+
</ul>
|
77 |
+
</div>
|
78 |
+
|
79 |
+
<div>
|
80 |
+
<h3 className="text-white font-medium mb-4">Car Categories</h3>
|
81 |
+
<ul className="space-y-2">
|
82 |
+
<li>
|
83 |
+
<Link to="/vehicles?type=sedan" className="text-gray-400 hover:text-white transition-colors text-sm">
|
84 |
+
Sedans
|
85 |
+
</Link>
|
86 |
+
</li>
|
87 |
+
<li>
|
88 |
+
<Link to="/vehicles?type=suv" className="text-gray-400 hover:text-white transition-colors text-sm">
|
89 |
+
SUVs
|
90 |
+
</Link>
|
91 |
+
</li>
|
92 |
+
<li>
|
93 |
+
<Link to="/vehicles?type=truck" className="text-gray-400 hover:text-white transition-colors text-sm">
|
94 |
+
Trucks
|
95 |
+
</Link>
|
96 |
+
</li>
|
97 |
+
<li>
|
98 |
+
<Link to="/vehicles?type=luxury" className="text-gray-400 hover:text-white transition-colors text-sm">
|
99 |
+
Luxury
|
100 |
+
</Link>
|
101 |
+
</li>
|
102 |
+
<li>
|
103 |
+
<Link to="/vehicles?type=sports" className="text-gray-400 hover:text-white transition-colors text-sm">
|
104 |
+
Sports Cars
|
105 |
+
</Link>
|
106 |
+
</li>
|
107 |
+
</ul>
|
108 |
+
</div>
|
109 |
+
|
110 |
+
<div>
|
111 |
+
<h3 className="text-white font-medium mb-4">Contact Info</h3>
|
112 |
+
<ul className="space-y-2">
|
113 |
+
<li className="text-gray-400 text-sm">123 Main Street, City, CA 94105</li>
|
114 |
+
<li className="text-gray-400 text-sm">+1 (555) 123-4567</li>
|
115 |
+
<li>
|
116 |
+
<a href="mailto:[email protected]" className="text-gray-400 hover:text-white transition-colors text-sm">
|
117 | |
118 |
+
</a>
|
119 |
+
</li>
|
120 |
+
</ul>
|
121 |
+
</div>
|
122 |
+
</div>
|
123 |
+
|
124 |
+
<div className="border-t border-gray-800 mt-10 pt-6 flex flex-col md:flex-row justify-between items-center">
|
125 |
+
<p className="text-gray-500 text-sm">© 2025 CarFleet. All rights reserved.</p>
|
126 |
+
<div className="flex space-x-6 mt-4 md:mt-0">
|
127 |
+
<Link to="/terms" className="text-gray-500 hover:text-white transition-colors text-sm">
|
128 |
+
Terms of Service
|
129 |
+
</Link>
|
130 |
+
<Link to="/privacy" className="text-gray-500 hover:text-white transition-colors text-sm">
|
131 |
+
Privacy Policy
|
132 |
+
</Link>
|
133 |
+
<Link to="/faq" className="text-gray-500 hover:text-white transition-colors text-sm">
|
134 |
+
FAQ
|
135 |
+
</Link>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
</div>
|
139 |
+
</footer>
|
140 |
+
);
|
141 |
+
};
|
142 |
+
|
143 |
+
export default Footer;
|
HomePage.tsx
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Link } from 'react-router-dom';
|
2 |
+
import { ArrowRight, ShieldCheck, Zap, Calendar } from 'lucide-react';
|
3 |
+
|
4 |
+
const HomePage = () => {
|
5 |
+
return (
|
6 |
+
<div className="bg-[#0a0a0a] text-white">
|
7 |
+
{/* Hero Section */}
|
8 |
+
<section className="relative py-20 overflow-hidden">
|
9 |
+
<div className="absolute inset-0 bg-gradient-to-r from-blue-900/20 to-purple-900/20 z-0"></div>
|
10 |
+
<div className="container mx-auto px-4 relative z-10">
|
11 |
+
<div className="max-w-3xl mx-auto text-center">
|
12 |
+
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold mb-6">
|
13 |
+
Rent Your Dream<br />Electric Vehicle
|
14 |
+
</h1>
|
15 |
+
<p className="text-gray-300 text-lg md:text-xl mb-8">
|
16 |
+
Experience the future of transportation with our premium electric vehicle
|
17 |
+
rental service. Zero emissions, maximum performance.
|
18 |
+
</p>
|
19 |
+
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
20 |
+
<Link
|
21 |
+
to="/vehicles"
|
22 |
+
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-md font-medium transition-colors"
|
23 |
+
>
|
24 |
+
Browse Electric Vehicles
|
25 |
+
</Link>
|
26 |
+
<Link
|
27 |
+
to="/how-it-works"
|
28 |
+
className="bg-transparent border border-blue-600 text-blue-600 hover:bg-blue-600/10 px-6 py-3 rounded-md font-medium transition-colors"
|
29 |
+
>
|
30 |
+
Learn More
|
31 |
+
</Link>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
</section>
|
36 |
+
|
37 |
+
{/* Features Section */}
|
38 |
+
<section className="py-16 bg-[#0c0c0c]">
|
39 |
+
<div className="container mx-auto px-4">
|
40 |
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
41 |
+
<div className="bg-[#161617] p-6 rounded-lg flex flex-col items-center text-center">
|
42 |
+
<div className="bg-blue-600/20 p-3 rounded-full mb-4">
|
43 |
+
<ShieldCheck className="text-blue-500 w-8 h-8" />
|
44 |
+
</div>
|
45 |
+
<h3 className="text-xl font-semibold mb-2">Safe & Secure</h3>
|
46 |
+
<p className="text-gray-400">
|
47 |
+
Premium vehicles with advanced safety features
|
48 |
+
</p>
|
49 |
+
</div>
|
50 |
+
|
51 |
+
<div className="bg-[#161617] p-6 rounded-lg flex flex-col items-center text-center">
|
52 |
+
<div className="bg-blue-600/20 p-3 rounded-full mb-4">
|
53 |
+
<Zap className="text-blue-500 w-8 h-8" />
|
54 |
+
</div>
|
55 |
+
<h3 className="text-xl font-semibold mb-2">100% Electric</h3>
|
56 |
+
<p className="text-gray-400">
|
57 |
+
Zero emissions, maximum performance
|
58 |
+
</p>
|
59 |
+
</div>
|
60 |
+
|
61 |
+
<div className="bg-[#161617] p-6 rounded-lg flex flex-col items-center text-center">
|
62 |
+
<div className="bg-blue-600/20 p-3 rounded-full mb-4">
|
63 |
+
<Calendar className="text-blue-500 w-8 h-8" />
|
64 |
+
</div>
|
65 |
+
<h3 className="text-xl font-semibold mb-2">Easy Booking</h3>
|
66 |
+
<p className="text-gray-400">
|
67 |
+
Book your ride in minutes
|
68 |
+
</p>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
</section>
|
73 |
+
|
74 |
+
{/* Experience Section */}
|
75 |
+
<section className="py-20">
|
76 |
+
<div className="container mx-auto px-4">
|
77 |
+
<div className="max-w-3xl mx-auto text-center mb-12">
|
78 |
+
<h2 className="text-3xl md:text-4xl font-bold mb-4">
|
79 |
+
Experience Electric Luxury
|
80 |
+
</h2>
|
81 |
+
<p className="text-gray-300">
|
82 |
+
Discover our lineup of premium electric vehicles. Cutting-edge technology,
|
83 |
+
breathtaking design, and zero emissions. Explore and reserve your Tesla today.
|
84 |
+
</p>
|
85 |
+
</div>
|
86 |
+
|
87 |
+
{/* Vehicle Cards will go here */}
|
88 |
+
<div className="flex justify-center mt-12">
|
89 |
+
<Link
|
90 |
+
to="/vehicles"
|
91 |
+
className="flex items-center text-blue-500 hover:text-blue-400 transition-colors font-medium"
|
92 |
+
>
|
93 |
+
<span>View all vehicles</span>
|
94 |
+
<ArrowRight className="ml-2 w-5 h-5" />
|
95 |
+
</Link>
|
96 |
+
</div>
|
97 |
+
</div>
|
98 |
+
</section>
|
99 |
+
</div>
|
100 |
+
);
|
101 |
+
};
|
102 |
+
|
103 |
+
export default HomePage;
|
LocationsPage.tsx
ADDED
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState } from 'react';
|
2 |
+
import { Search, MapPin } from 'lucide-react';
|
3 |
+
|
4 |
+
interface Location {
|
5 |
+
id: string;
|
6 |
+
name: string;
|
7 |
+
address: string;
|
8 |
+
city: string;
|
9 |
+
state: string;
|
10 |
+
zip: string;
|
11 |
+
hours: string;
|
12 |
+
phone: string;
|
13 |
+
amenities: string[];
|
14 |
+
coordinates: {
|
15 |
+
lat: number;
|
16 |
+
lng: number;
|
17 |
+
};
|
18 |
+
}
|
19 |
+
|
20 |
+
const locations: Location[] = [
|
21 |
+
{
|
22 |
+
id: 'sf-downtown',
|
23 |
+
name: 'San Francisco Downtown',
|
24 |
+
address: '123 Market Street',
|
25 |
+
city: 'San Francisco',
|
26 |
+
state: 'CA',
|
27 |
+
zip: '94105',
|
28 |
+
hours: 'Mon-Sun: 7am-10pm',
|
29 |
+
phone: '(415) 555-1234',
|
30 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Accessories Shop', 'Restrooms'],
|
31 |
+
coordinates: {
|
32 |
+
lat: 37.7749,
|
33 |
+
lng: -122.4194,
|
34 |
+
},
|
35 |
+
},
|
36 |
+
{
|
37 |
+
id: 'la-century',
|
38 |
+
name: 'Los Angeles Century City',
|
39 |
+
address: '1888 Century Park East',
|
40 |
+
city: 'Los Angeles',
|
41 |
+
state: 'CA',
|
42 |
+
zip: '90067',
|
43 |
+
hours: 'Mon-Sun: 8am-9pm',
|
44 |
+
phone: '(310) 555-6789',
|
45 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Car Wash', 'Restrooms'],
|
46 |
+
coordinates: {
|
47 |
+
lat: 34.0522,
|
48 |
+
lng: -118.2437,
|
49 |
+
},
|
50 |
+
},
|
51 |
+
{
|
52 |
+
id: 'nyc-manhattan',
|
53 |
+
name: 'New York - Manhattan',
|
54 |
+
address: '347 West 34th Street',
|
55 |
+
city: 'New York',
|
56 |
+
state: 'NY',
|
57 |
+
zip: '10001',
|
58 |
+
hours: 'Mon-Sun: 9am-8pm',
|
59 |
+
phone: '(212) 555-8901',
|
60 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Accessories Shop', 'Coffee Bar'],
|
61 |
+
coordinates: {
|
62 |
+
lat: 40.7128,
|
63 |
+
lng: -74.006,
|
64 |
+
},
|
65 |
+
},
|
66 |
+
{
|
67 |
+
id: 'miami-beach',
|
68 |
+
name: 'Miami Beach',
|
69 |
+
address: '1100 Collins Avenue',
|
70 |
+
city: 'Miami Beach',
|
71 |
+
state: 'FL',
|
72 |
+
zip: '33139',
|
73 |
+
hours: 'Mon-Sun: 8am-10pm',
|
74 |
+
phone: '(305) 555-2345',
|
75 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Car Wash', 'Coffee Bar', 'Restrooms'],
|
76 |
+
coordinates: {
|
77 |
+
lat: 25.7617,
|
78 |
+
lng: -80.1918,
|
79 |
+
},
|
80 |
+
},
|
81 |
+
{
|
82 |
+
id: 'chicago-loop',
|
83 |
+
name: 'Chicago - The Loop',
|
84 |
+
address: '333 South Wabash Avenue',
|
85 |
+
city: 'Chicago',
|
86 |
+
state: 'IL',
|
87 |
+
zip: '60604',
|
88 |
+
hours: 'Mon-Sun: 7am-9pm',
|
89 |
+
phone: '(312) 555-3456',
|
90 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Restrooms'],
|
91 |
+
coordinates: {
|
92 |
+
lat: 41.8781,
|
93 |
+
lng: -87.6298,
|
94 |
+
},
|
95 |
+
},
|
96 |
+
{
|
97 |
+
id: 'seattle-downtown',
|
98 |
+
name: 'Seattle Downtown',
|
99 |
+
address: '500 Pine Street',
|
100 |
+
city: 'Seattle',
|
101 |
+
state: 'WA',
|
102 |
+
zip: '98101',
|
103 |
+
hours: 'Mon-Sun: 7am-8pm',
|
104 |
+
phone: '(206) 555-4567',
|
105 |
+
amenities: ['Tesla Superchargers', 'Waiting Lounge', 'Accessories Shop', 'Restrooms'],
|
106 |
+
coordinates: {
|
107 |
+
lat: 47.6062,
|
108 |
+
lng: -122.3321,
|
109 |
+
},
|
110 |
+
},
|
111 |
+
];
|
112 |
+
|
113 |
+
const cities = ['All Cities', 'San Francisco', 'Los Angeles', 'New York', 'Miami Beach', 'Chicago', 'Seattle'];
|
114 |
+
|
115 |
+
const LocationsPage = () => {
|
116 |
+
const [searchQuery, setSearchQuery] = useState('');
|
117 |
+
const [selectedCity, setSelectedCity] = useState('All Cities');
|
118 |
+
const [selectedLocation, setSelectedLocation] = useState<Location | null>(null);
|
119 |
+
|
120 |
+
const filteredLocations = locations.filter((location) => {
|
121 |
+
const matchesSearch = searchQuery === '' ||
|
122 |
+
location.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
123 |
+
location.address.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
124 |
+
location.city.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
125 |
+
location.state.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
126 |
+
location.zip.toLowerCase().includes(searchQuery.toLowerCase());
|
127 |
+
|
128 |
+
const matchesCity = selectedCity === 'All Cities' || location.city === selectedCity;
|
129 |
+
|
130 |
+
return matchesSearch && matchesCity;
|
131 |
+
});
|
132 |
+
|
133 |
+
const handleLocationClick = (location: Location) => {
|
134 |
+
setSelectedLocation(location);
|
135 |
+
};
|
136 |
+
|
137 |
+
return (
|
138 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
139 |
+
<div className="container mx-auto px-4 py-12">
|
140 |
+
{/* Header */}
|
141 |
+
<div className="max-w-3xl mx-auto text-center mb-12">
|
142 |
+
<h1 className="text-4xl md:text-5xl font-bold mb-4">Our Locations</h1>
|
143 |
+
<p className="text-gray-300 text-lg">
|
144 |
+
Find the nearest pickup and dropoff location for your electric vehicle rental.
|
145 |
+
</p>
|
146 |
+
</div>
|
147 |
+
|
148 |
+
{/* Search and Filter */}
|
149 |
+
<div className="mb-8">
|
150 |
+
<div className="flex flex-col md:flex-row gap-4 mb-4">
|
151 |
+
<div className="relative flex-1">
|
152 |
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
153 |
+
<Search className="h-5 w-5 text-gray-400" />
|
154 |
+
</div>
|
155 |
+
<input
|
156 |
+
type="text"
|
157 |
+
placeholder="Search by location name, address, or zip code"
|
158 |
+
className="block w-full pl-10 pr-3 py-3 bg-[#161617] border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent text-white"
|
159 |
+
value={searchQuery}
|
160 |
+
onChange={(e) => setSearchQuery(e.target.value)}
|
161 |
+
/>
|
162 |
+
</div>
|
163 |
+
<select
|
164 |
+
value={selectedCity}
|
165 |
+
onChange={(e) => setSelectedCity(e.target.value)}
|
166 |
+
className="bg-[#161617] border border-gray-700 text-white rounded-md py-3 px-4 appearance-none md:max-w-xs focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent"
|
167 |
+
>
|
168 |
+
{cities.map((city) => (
|
169 |
+
<option key={city} value={city}>
|
170 |
+
{city}
|
171 |
+
</option>
|
172 |
+
))}
|
173 |
+
</select>
|
174 |
+
</div>
|
175 |
+
</div>
|
176 |
+
|
177 |
+
{/* Main Content */}
|
178 |
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
179 |
+
{/* Locations List */}
|
180 |
+
<div className="lg:col-span-1">
|
181 |
+
<div className="bg-[#161617] rounded-lg overflow-hidden border border-gray-800">
|
182 |
+
<div className="p-4 border-b border-gray-800">
|
183 |
+
<h3 className="font-medium">{filteredLocations.length} {filteredLocations.length === 1 ? 'Location' : 'Locations'} Found</h3>
|
184 |
+
</div>
|
185 |
+
<div className="divide-y divide-gray-800 max-h-[600px] overflow-y-auto">
|
186 |
+
{filteredLocations.length > 0 ? (
|
187 |
+
filteredLocations.map((location) => (
|
188 |
+
<button
|
189 |
+
key={location.id}
|
190 |
+
onClick={() => handleLocationClick(location)}
|
191 |
+
className={`w-full px-4 py-3 flex items-start text-left transition-colors hover:bg-gray-800 ${
|
192 |
+
selectedLocation?.id === location.id ? 'bg-gray-800' : ''
|
193 |
+
}`}
|
194 |
+
>
|
195 |
+
<MapPin className="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5 mr-3" />
|
196 |
+
<div>
|
197 |
+
<h4 className="font-medium">{location.name}</h4>
|
198 |
+
<p className="text-gray-400 text-sm">{location.address}, {location.city}, {location.state} {location.zip}</p>
|
199 |
+
<p className="text-gray-500 text-xs mt-1">{location.hours}</p>
|
200 |
+
</div>
|
201 |
+
</button>
|
202 |
+
))
|
203 |
+
) : (
|
204 |
+
<div className="p-6 text-center">
|
205 |
+
<p className="text-gray-400">No locations found matching your search criteria.</p>
|
206 |
+
</div>
|
207 |
+
)}
|
208 |
+
</div>
|
209 |
+
</div>
|
210 |
+
</div>
|
211 |
+
|
212 |
+
{/* Map and Location Details */}
|
213 |
+
<div className="lg:col-span-2">
|
214 |
+
{selectedLocation ? (
|
215 |
+
<div className="grid grid-cols-1 gap-6">
|
216 |
+
{/* Map Placeholder */}
|
217 |
+
<div className="bg-[#161617] rounded-lg overflow-hidden border border-gray-800 h-[300px] flex items-center justify-center">
|
218 |
+
<div className="text-center px-4">
|
219 |
+
<p className="text-gray-400 mb-2">Interactive map would be displayed here with location:</p>
|
220 |
+
<p className="font-medium">{selectedLocation.name}</p>
|
221 |
+
<p className="text-sm text-gray-500">Coordinates: {selectedLocation.coordinates.lat}, {selectedLocation.coordinates.lng}</p>
|
222 |
+
</div>
|
223 |
+
</div>
|
224 |
+
|
225 |
+
{/* Location Details */}
|
226 |
+
<div className="bg-[#161617] rounded-lg overflow-hidden border border-gray-800">
|
227 |
+
<div className="p-5 border-b border-gray-800">
|
228 |
+
<h3 className="text-xl font-bold">{selectedLocation.name}</h3>
|
229 |
+
<p className="text-gray-400 mt-1">{selectedLocation.address}, {selectedLocation.city}, {selectedLocation.state} {selectedLocation.zip}</p>
|
230 |
+
</div>
|
231 |
+
<div className="p-5 grid grid-cols-1 md:grid-cols-2 gap-6">
|
232 |
+
<div>
|
233 |
+
<h4 className="text-lg font-medium mb-3">Hours & Contact</h4>
|
234 |
+
<div className="space-y-3">
|
235 |
+
<div>
|
236 |
+
<p className="text-gray-400 text-sm">Hours</p>
|
237 |
+
<p>{selectedLocation.hours}</p>
|
238 |
+
</div>
|
239 |
+
<div>
|
240 |
+
<p className="text-gray-400 text-sm">Phone</p>
|
241 |
+
<p>{selectedLocation.phone}</p>
|
242 |
+
</div>
|
243 |
+
</div>
|
244 |
+
</div>
|
245 |
+
<div>
|
246 |
+
<h4 className="text-lg font-medium mb-3">Amenities</h4>
|
247 |
+
<ul className="space-y-2">
|
248 |
+
{selectedLocation.amenities.map((amenity, index) => (
|
249 |
+
<li key={index} className="flex items-center gap-2">
|
250 |
+
<div className="h-2 w-2 rounded-full bg-blue-500"></div>
|
251 |
+
<span>{amenity}</span>
|
252 |
+
</li>
|
253 |
+
))}
|
254 |
+
</ul>
|
255 |
+
</div>
|
256 |
+
</div>
|
257 |
+
<div className="p-5 bg-[#1c1c1e] border-t border-gray-800">
|
258 |
+
<button className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-md transition-colors">
|
259 |
+
Book a Vehicle at This Location
|
260 |
+
</button>
|
261 |
+
</div>
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
) : (
|
265 |
+
<div className="bg-[#161617] rounded-lg border border-gray-800 h-full flex items-center justify-center p-10">
|
266 |
+
<div className="text-center max-w-md">
|
267 |
+
<MapPin className="h-12 w-12 text-blue-500 mb-4 mx-auto" />
|
268 |
+
<h3 className="text-xl font-bold mb-2">Select a Location</h3>
|
269 |
+
<p className="text-gray-400">
|
270 |
+
Choose a location from the list to view details and availability information.
|
271 |
+
</p>
|
272 |
+
</div>
|
273 |
+
</div>
|
274 |
+
)}
|
275 |
+
</div>
|
276 |
+
</div>
|
277 |
+
</div>
|
278 |
+
</div>
|
279 |
+
);
|
280 |
+
};
|
281 |
+
|
282 |
+
export default LocationsPage;
|
Navbar.tsx
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState } from 'react';
|
2 |
+
import { Link, useLocation } from 'react-router-dom';
|
3 |
+
import { Menu, X } from 'lucide-react';
|
4 |
+
|
5 |
+
const Navbar = () => {
|
6 |
+
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
7 |
+
const location = useLocation();
|
8 |
+
|
9 |
+
const toggleMenu = () => {
|
10 |
+
setIsMenuOpen(!isMenuOpen);
|
11 |
+
};
|
12 |
+
|
13 |
+
const navLinks = [
|
14 |
+
{ name: 'Home', path: '/' },
|
15 |
+
{ name: 'Vehicles', path: '/vehicles' },
|
16 |
+
{ name: 'Plans', path: '/pricing' },
|
17 |
+
{ name: 'Locations', path: '/locations' },
|
18 |
+
{ name: 'About', path: '/about' },
|
19 |
+
];
|
20 |
+
|
21 |
+
return (
|
22 |
+
<header className="bg-[#0a0a0a]/80 backdrop-blur-md sticky top-0 z-50 border-b border-gray-800">
|
23 |
+
<div className="container mx-auto px-4 md:px-6 py-4">
|
24 |
+
<div className="flex items-center justify-between">
|
25 |
+
<Link to="/" className="flex items-center space-x-2">
|
26 |
+
<span className="text-blue-500 text-2xl font-bold">Unity Fleet</span>
|
27 |
+
</Link>
|
28 |
+
|
29 |
+
{/* Desktop Navigation */}
|
30 |
+
<nav className="hidden md:flex items-center space-x-10">
|
31 |
+
{navLinks.map((link) => (
|
32 |
+
<Link
|
33 |
+
key={link.path}
|
34 |
+
to={link.path}
|
35 |
+
className={`text-sm font-medium transition-colors hover:text-blue-500 ${
|
36 |
+
location.pathname === link.path ? 'text-blue-500' : 'text-gray-300'
|
37 |
+
}`}
|
38 |
+
>
|
39 |
+
{link.name}
|
40 |
+
</Link>
|
41 |
+
))}
|
42 |
+
</nav>
|
43 |
+
|
44 |
+
<div className="hidden md:flex items-center space-x-4">
|
45 |
+
<Link
|
46 |
+
to="/dashboard"
|
47 |
+
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md text-white text-sm font-medium transition-colors"
|
48 |
+
>
|
49 |
+
Dashboard
|
50 |
+
</Link>
|
51 |
+
</div>
|
52 |
+
|
53 |
+
{/* Mobile Menu Button */}
|
54 |
+
<button
|
55 |
+
onClick={toggleMenu}
|
56 |
+
className="md:hidden text-gray-300 hover:text-white"
|
57 |
+
aria-label="Toggle Menu"
|
58 |
+
>
|
59 |
+
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
60 |
+
</button>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
|
64 |
+
{/* Mobile Navigation */}
|
65 |
+
{isMenuOpen && (
|
66 |
+
<div className="md:hidden bg-[#0a0a0a] border-t border-gray-800">
|
67 |
+
<div className="container mx-auto px-4 py-4">
|
68 |
+
<nav className="flex flex-col space-y-4">
|
69 |
+
{navLinks.map((link) => (
|
70 |
+
<Link
|
71 |
+
key={link.path}
|
72 |
+
to={link.path}
|
73 |
+
className={`text-sm font-medium transition-colors hover:text-blue-500 ${
|
74 |
+
location.pathname === link.path ? 'text-blue-500' : 'text-gray-300'
|
75 |
+
}`}
|
76 |
+
onClick={() => setIsMenuOpen(false)}
|
77 |
+
>
|
78 |
+
{link.name}
|
79 |
+
</Link>
|
80 |
+
))}
|
81 |
+
<Link
|
82 |
+
to="/dashboard"
|
83 |
+
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-md text-white text-sm font-medium transition-colors text-center"
|
84 |
+
onClick={() => setIsMenuOpen(false)}
|
85 |
+
>
|
86 |
+
Dashboard
|
87 |
+
</Link>
|
88 |
+
</nav>
|
89 |
+
</div>
|
90 |
+
</div>
|
91 |
+
)}
|
92 |
+
</header>
|
93 |
+
);
|
94 |
+
};
|
95 |
+
|
96 |
+
export default Navbar;
|
PricingPage.tsx
ADDED
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState } from 'react';
|
2 |
+
import { Link } from 'react-router-dom';
|
3 |
+
import { Check } from 'lucide-react';
|
4 |
+
|
5 |
+
interface Plan {
|
6 |
+
id: string;
|
7 |
+
name: string;
|
8 |
+
description: string;
|
9 |
+
pricing: {
|
10 |
+
monthly: number;
|
11 |
+
annually: number;
|
12 |
+
};
|
13 |
+
features: string[];
|
14 |
+
isPopular?: boolean;
|
15 |
+
}
|
16 |
+
|
17 |
+
const plans: Plan[] = [
|
18 |
+
{
|
19 |
+
id: 'basic',
|
20 |
+
name: 'Basic',
|
21 |
+
description: 'For occasional drivers who need a car for a few days per month.',
|
22 |
+
pricing: {
|
23 |
+
monthly: 399,
|
24 |
+
annually: 4199,
|
25 |
+
},
|
26 |
+
features: [
|
27 |
+
'Up to 3 days per month',
|
28 |
+
'Access to Model 3 and Model Y',
|
29 |
+
'Basic insurance coverage',
|
30 |
+
'Charging included',
|
31 |
+
'Mileage limit: 1,000 miles/month',
|
32 |
+
'Roadside assistance',
|
33 |
+
],
|
34 |
+
},
|
35 |
+
{
|
36 |
+
id: 'premium',
|
37 |
+
name: 'Premium',
|
38 |
+
description: 'For regular drivers who need more flexibility and premium vehicles.',
|
39 |
+
pricing: {
|
40 |
+
monthly: 799,
|
41 |
+
annually: 8599,
|
42 |
+
},
|
43 |
+
features: [
|
44 |
+
'Up to 9 days per month',
|
45 |
+
'Access to all Tesla models',
|
46 |
+
'Full insurance coverage',
|
47 |
+
'Charging included',
|
48 |
+
'Mileage limit: 2,500 miles/month',
|
49 |
+
'Roadside assistance',
|
50 |
+
'Priority customer support',
|
51 |
+
'Flexible scheduling',
|
52 |
+
],
|
53 |
+
isPopular: true,
|
54 |
+
},
|
55 |
+
{
|
56 |
+
id: 'unlimited',
|
57 |
+
name: 'Unlimited',
|
58 |
+
description: 'For those who want unrestricted access to our entire fleet.',
|
59 |
+
pricing: {
|
60 |
+
monthly: 1499,
|
61 |
+
annually: 15999,
|
62 |
+
},
|
63 |
+
features: [
|
64 |
+
'Unlimited access (30 days/month)',
|
65 |
+
'Access to all Tesla models including Cybertruck',
|
66 |
+
'Full insurance coverage',
|
67 |
+
'Unlimited charging',
|
68 |
+
'Unlimited mileage',
|
69 |
+
'Roadside assistance',
|
70 |
+
'Premium customer support',
|
71 |
+
'Flexible scheduling',
|
72 |
+
'Vehicle delivery and pickup',
|
73 |
+
'Exclusive event invitations',
|
74 |
+
],
|
75 |
+
},
|
76 |
+
];
|
77 |
+
|
78 |
+
const faqItems = [
|
79 |
+
{
|
80 |
+
question: 'How does the subscription work?',
|
81 |
+
answer: 'Our subscription plans give you access to Tesla vehicles for a fixed monthly or annual fee. Choose your plan based on how often you need a car, select your preferred vehicle, and we'll handle the rest.'
|
82 |
+
},
|
83 |
+
{
|
84 |
+
question: 'Can I switch between different Tesla models?',
|
85 |
+
answer: 'Yes! Depending on your plan, you can switch between different Tesla models. Premium and Unlimited members have access to our entire fleet and can change vehicles up to twice per month.'
|
86 |
+
},
|
87 |
+
{
|
88 |
+
question: 'What does the insurance cover?',
|
89 |
+
answer: 'Our basic insurance covers liability and collision with a deductible. Premium and Unlimited plans include comprehensive coverage with a lower deductible, protecting you from almost any incident.'
|
90 |
+
},
|
91 |
+
{
|
92 |
+
question: 'Is charging included in the subscription?',
|
93 |
+
answer: 'Yes, charging is included in all subscription plans. Basic and Premium plans include charging at Tesla Superchargers and partner network stations, while Unlimited includes home charging equipment installation.'
|
94 |
+
},
|
95 |
+
{
|
96 |
+
question: 'How do I cancel my subscription?',
|
97 |
+
answer: 'You can cancel your subscription anytime through your account dashboard. Monthly subscriptions can be canceled with a 7-day notice, while annual subscriptions can be canceled with a 30-day notice with prorated refunds.'
|
98 |
+
}
|
99 |
+
];
|
100 |
+
|
101 |
+
const PricingPage = () => {
|
102 |
+
const [billingCycle, setBillingCycle] = useState<'monthly' | 'annually'>('monthly');
|
103 |
+
const [expandedFaqIndex, setExpandedFaqIndex] = useState<number | null>(null);
|
104 |
+
|
105 |
+
const toggleFaq = (index: number) => {
|
106 |
+
setExpandedFaqIndex(expandedFaqIndex === index ? null : index);
|
107 |
+
};
|
108 |
+
|
109 |
+
return (
|
110 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
111 |
+
<div className="container mx-auto px-4 py-16">
|
112 |
+
{/* Header */}
|
113 |
+
<div className="max-w-3xl mx-auto text-center mb-12">
|
114 |
+
<h1 className="text-4xl md:text-5xl font-bold mb-4">Choose Your Membership Plan</h1>
|
115 |
+
<p className="text-gray-300 text-lg">
|
116 |
+
Flexible plans designed to fit your lifestyle. Subscribe monthly or annually for the best value.
|
117 |
+
</p>
|
118 |
+
</div>
|
119 |
+
|
120 |
+
{/* Billing Toggle */}
|
121 |
+
<div className="flex justify-center mb-12">
|
122 |
+
<div className="bg-[#161617] inline-flex rounded-lg p-1">
|
123 |
+
<button
|
124 |
+
onClick={() => setBillingCycle('monthly')}
|
125 |
+
className={`px-4 py-2 rounded-md text-sm font-medium transition-all ${
|
126 |
+
billingCycle === 'monthly'
|
127 |
+
? 'bg-blue-600 text-white'
|
128 |
+
: 'text-gray-400 hover:text-white'
|
129 |
+
}`}
|
130 |
+
>
|
131 |
+
Monthly
|
132 |
+
</button>
|
133 |
+
<button
|
134 |
+
onClick={() => setBillingCycle('annually')}
|
135 |
+
className={`px-4 py-2 rounded-md text-sm font-medium transition-all ${
|
136 |
+
billingCycle === 'annually'
|
137 |
+
? 'bg-blue-600 text-white'
|
138 |
+
: 'text-gray-400 hover:text-white'
|
139 |
+
}`}
|
140 |
+
>
|
141 |
+
Annually
|
142 |
+
<span className="ml-2 bg-green-600/20 text-green-500 px-1.5 py-0.5 text-xs rounded-sm">
|
143 |
+
Save 10%
|
144 |
+
</span>
|
145 |
+
</button>
|
146 |
+
</div>
|
147 |
+
</div>
|
148 |
+
|
149 |
+
{/* Pricing Cards */}
|
150 |
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16">
|
151 |
+
{plans.map((plan) => (
|
152 |
+
<div
|
153 |
+
key={plan.id}
|
154 |
+
className={`bg-[#161617] rounded-lg overflow-hidden border ${
|
155 |
+
plan.isPopular ? 'border-blue-600' : 'border-gray-800'
|
156 |
+
} relative`}
|
157 |
+
>
|
158 |
+
{plan.isPopular && (
|
159 |
+
<div className="absolute top-0 right-0 bg-blue-600 text-xs font-bold uppercase px-3 py-1 rotate-0">
|
160 |
+
Most Popular
|
161 |
+
</div>
|
162 |
+
)}
|
163 |
+
<div className="p-6">
|
164 |
+
<h3 className="text-xl font-bold mb-2">{plan.name}</h3>
|
165 |
+
<p className="text-gray-400 text-sm mb-6">{plan.description}</p>
|
166 |
+
<div className="mb-6">
|
167 |
+
<span className="text-4xl font-bold">
|
168 |
+
${billingCycle === 'monthly' ? plan.pricing.monthly : plan.pricing.annually}
|
169 |
+
</span>
|
170 |
+
<span className="text-gray-400 text-sm">
|
171 |
+
/{billingCycle === 'monthly' ? 'month' : 'year'}
|
172 |
+
</span>
|
173 |
+
</div>
|
174 |
+
<Link
|
175 |
+
to={`/pricing/${plan.id}`}
|
176 |
+
className={`block text-center py-3 px-4 rounded-md font-medium transition-colors ${
|
177 |
+
plan.isPopular
|
178 |
+
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
179 |
+
: 'bg-gray-800 hover:bg-gray-700 text-white'
|
180 |
+
}`}
|
181 |
+
>
|
182 |
+
Choose Plan
|
183 |
+
</Link>
|
184 |
+
</div>
|
185 |
+
<div className="border-t border-gray-800 p-6">
|
186 |
+
<h4 className="font-medium mb-4">What's included:</h4>
|
187 |
+
<ul className="space-y-3">
|
188 |
+
{plan.features.map((feature, index) => (
|
189 |
+
<li key={index} className="flex items-start gap-2">
|
190 |
+
<div className="flex-shrink-0 w-5 h-5 rounded-full bg-green-600/20 flex items-center justify-center mt-0.5">
|
191 |
+
<Check className="text-green-500 w-3 h-3" />
|
192 |
+
</div>
|
193 |
+
<span className="text-sm text-gray-300">{feature}</span>
|
194 |
+
</li>
|
195 |
+
))}
|
196 |
+
</ul>
|
197 |
+
</div>
|
198 |
+
</div>
|
199 |
+
))}
|
200 |
+
</div>
|
201 |
+
|
202 |
+
{/* FAQ Section */}
|
203 |
+
<div className="max-w-3xl mx-auto">
|
204 |
+
<h2 className="text-2xl font-bold mb-8 text-center">Frequently Asked Questions</h2>
|
205 |
+
<div className="divide-y divide-gray-800">
|
206 |
+
{faqItems.map((faq, index) => (
|
207 |
+
<div key={index} className="py-5">
|
208 |
+
<button
|
209 |
+
onClick={() => toggleFaq(index)}
|
210 |
+
className="flex justify-between items-center w-full text-left font-medium"
|
211 |
+
>
|
212 |
+
<span>{faq.question}</span>
|
213 |
+
<span className="ml-6 flex-shrink-0">
|
214 |
+
{expandedFaqIndex === index ? (
|
215 |
+
<svg className="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
216 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
|
217 |
+
</svg>
|
218 |
+
) : (
|
219 |
+
<svg className="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
220 |
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
221 |
+
</svg>
|
222 |
+
)}
|
223 |
+
</span>
|
224 |
+
</button>
|
225 |
+
{expandedFaqIndex === index && (
|
226 |
+
<div className="mt-2 text-gray-400 text-sm">
|
227 |
+
<p>{faq.answer}</p>
|
228 |
+
</div>
|
229 |
+
)}
|
230 |
+
</div>
|
231 |
+
))}
|
232 |
+
</div>
|
233 |
+
</div>
|
234 |
+
</div>
|
235 |
+
</div>
|
236 |
+
);
|
237 |
+
};
|
238 |
+
|
239 |
+
export default PricingPage;
|
README.md
CHANGED
@@ -1,13 +1,54 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# React + TypeScript + Vite
|
2 |
+
|
3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
4 |
+
|
5 |
+
Currently, two official plugins are available:
|
6 |
+
|
7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
9 |
+
|
10 |
+
## Expanding the ESLint configuration
|
11 |
+
|
12 |
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
13 |
+
|
14 |
+
```js
|
15 |
+
export default tseslint.config({
|
16 |
+
extends: [
|
17 |
+
// Remove ...tseslint.configs.recommended and replace with this
|
18 |
+
...tseslint.configs.recommendedTypeChecked,
|
19 |
+
// Alternatively, use this for stricter rules
|
20 |
+
...tseslint.configs.strictTypeChecked,
|
21 |
+
// Optionally, add this for stylistic rules
|
22 |
+
...tseslint.configs.stylisticTypeChecked,
|
23 |
+
],
|
24 |
+
languageOptions: {
|
25 |
+
// other options...
|
26 |
+
parserOptions: {
|
27 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
28 |
+
tsconfigRootDir: import.meta.dirname,
|
29 |
+
},
|
30 |
+
},
|
31 |
+
})
|
32 |
+
```
|
33 |
+
|
34 |
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
35 |
+
|
36 |
+
```js
|
37 |
+
// eslint.config.js
|
38 |
+
import reactX from 'eslint-plugin-react-x'
|
39 |
+
import reactDom from 'eslint-plugin-react-dom'
|
40 |
+
|
41 |
+
export default tseslint.config({
|
42 |
+
plugins: {
|
43 |
+
// Add the react-x and react-dom plugins
|
44 |
+
'react-x': reactX,
|
45 |
+
'react-dom': reactDom,
|
46 |
+
},
|
47 |
+
rules: {
|
48 |
+
// other rules...
|
49 |
+
// Enable its recommended typescript rules
|
50 |
+
...reactX.configs['recommended-typescript'].rules,
|
51 |
+
...reactDom.configs.recommended.rules,
|
52 |
+
},
|
53 |
+
})
|
54 |
+
```
|
VehicleCard.tsx
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState } from 'react';
|
2 |
+
import { Link } from 'react-router-dom';
|
3 |
+
import { Vehicle } from '@/data/vehicles';
|
4 |
+
import { ChevronRight } from 'lucide-react';
|
5 |
+
|
6 |
+
interface VehicleCardProps {
|
7 |
+
vehicle: Vehicle;
|
8 |
+
}
|
9 |
+
|
10 |
+
const VehicleCard = ({ vehicle }: VehicleCardProps) => {
|
11 |
+
const [isHovered, setIsHovered] = useState(false);
|
12 |
+
|
13 |
+
return (
|
14 |
+
<div
|
15 |
+
className="bg-[#161617] rounded-lg overflow-hidden transition-all duration-300 hover:shadow-xl hover:shadow-blue-900/10"
|
16 |
+
onMouseEnter={() => setIsHovered(true)}
|
17 |
+
onMouseLeave={() => setIsHovered(false)}
|
18 |
+
>
|
19 |
+
<div className="relative">
|
20 |
+
<div className={`h-52 bg-gradient-to-br from-[#202022] to-[#121214] overflow-hidden flex items-center justify-center transition-transform duration-500 ${isHovered ? 'scale-105' : 'scale-100'}`}>
|
21 |
+
<img
|
22 |
+
src={vehicle.image}
|
23 |
+
alt={`${vehicle.name} ${vehicle.model}`}
|
24 |
+
className="h-40 object-contain transform transition-all duration-500"
|
25 |
+
/>
|
26 |
+
</div>
|
27 |
+
<div className="absolute top-3 right-3 bg-blue-600/80 text-xs font-bold text-white px-2 py-1 rounded">
|
28 |
+
{vehicle.category}
|
29 |
+
</div>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div className="p-5">
|
33 |
+
<div className="mb-4">
|
34 |
+
<h3 className="text-xl font-bold text-white">
|
35 |
+
{vehicle.name} {vehicle.model}
|
36 |
+
</h3>
|
37 |
+
<p className="text-gray-400 text-sm mt-1 line-clamp-2">
|
38 |
+
{vehicle.description}
|
39 |
+
</p>
|
40 |
+
</div>
|
41 |
+
|
42 |
+
<div className="flex justify-between items-center mb-4">
|
43 |
+
<div>
|
44 |
+
<span className="text-2xl font-bold text-white">${vehicle.price}</span>
|
45 |
+
<span className="text-gray-400 text-sm">/day</span>
|
46 |
+
</div>
|
47 |
+
<div className="text-right">
|
48 |
+
<span className="text-gray-400 text-sm">Subscription</span>
|
49 |
+
<p className="text-gray-300 font-medium">${vehicle.pricePerMonth}/mo</p>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
|
53 |
+
<div className="grid grid-cols-2 gap-2 mb-5">
|
54 |
+
<div className="bg-[#1a1a1c] p-2 rounded text-center">
|
55 |
+
<p className="text-xs text-gray-400">Range</p>
|
56 |
+
<p className="text-sm font-medium text-white">{vehicle.specs.range}</p>
|
57 |
+
</div>
|
58 |
+
<div className="bg-[#1a1a1c] p-2 rounded text-center">
|
59 |
+
<p className="text-xs text-gray-400">0-60 mph</p>
|
60 |
+
<p className="text-sm font-medium text-white">{vehicle.specs.acceleration}</p>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
|
64 |
+
<Link
|
65 |
+
to={`/vehicles/${vehicle.id}`}
|
66 |
+
className="bg-blue-600 hover:bg-blue-700 w-full py-3 rounded-md font-medium transition-colors text-white flex items-center justify-center gap-1"
|
67 |
+
>
|
68 |
+
View Details
|
69 |
+
<ChevronRight size={16} />
|
70 |
+
</Link>
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
);
|
74 |
+
};
|
75 |
+
|
76 |
+
export default VehicleCard;
|
VehicleDetailPage.tsx
ADDED
@@ -0,0 +1,327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useEffect, useState } from 'react';
|
2 |
+
import { useParams, Link } from 'react-router-dom';
|
3 |
+
import { getVehicleById, Vehicle } from '@/data/vehicles';
|
4 |
+
import { ArrowLeft, Battery, Clock, Users, Zap, Check, Calendar, CreditCard } from 'lucide-react';
|
5 |
+
|
6 |
+
const VehicleDetailPage = () => {
|
7 |
+
const { id } = useParams<{ id: string }>();
|
8 |
+
const [vehicle, setVehicle] = useState<Vehicle | null>(null);
|
9 |
+
const [activeTab, setActiveTab] = useState<'overview' | 'specs' | 'features'>('overview');
|
10 |
+
const [rentalDuration, setRentalDuration] = useState(1); // days
|
11 |
+
const [isLoading, setIsLoading] = useState(true);
|
12 |
+
|
13 |
+
useEffect(() => {
|
14 |
+
if (id) {
|
15 |
+
const foundVehicle = getVehicleById(id);
|
16 |
+
setVehicle(foundVehicle || null);
|
17 |
+
setIsLoading(false);
|
18 |
+
}
|
19 |
+
}, [id]);
|
20 |
+
|
21 |
+
if (isLoading) {
|
22 |
+
return (
|
23 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen flex items-center justify-center">
|
24 |
+
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
|
25 |
+
</div>
|
26 |
+
);
|
27 |
+
}
|
28 |
+
|
29 |
+
if (!vehicle) {
|
30 |
+
return (
|
31 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
32 |
+
<div className="container mx-auto px-4 py-12 text-center">
|
33 |
+
<h2 className="text-2xl font-bold mb-4">Vehicle Not Found</h2>
|
34 |
+
<p className="text-gray-400 mb-6">Sorry, we couldn't find the vehicle you're looking for.</p>
|
35 |
+
<Link
|
36 |
+
to="/vehicles"
|
37 |
+
className="bg-blue-600 hover:bg-blue-700 px-5 py-2.5 rounded-md inline-flex items-center gap-2"
|
38 |
+
>
|
39 |
+
<ArrowLeft size={16} />
|
40 |
+
Back to Vehicles
|
41 |
+
</Link>
|
42 |
+
</div>
|
43 |
+
</div>
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
const totalRentalPrice = vehicle.price * rentalDuration;
|
48 |
+
|
49 |
+
return (
|
50 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
51 |
+
<div className="container mx-auto px-4 py-12">
|
52 |
+
{/* Back navigation */}
|
53 |
+
<Link
|
54 |
+
to="/vehicles"
|
55 |
+
className="inline-flex items-center gap-2 text-gray-300 hover:text-white mb-8"
|
56 |
+
>
|
57 |
+
<ArrowLeft size={16} />
|
58 |
+
Back to Vehicles
|
59 |
+
</Link>
|
60 |
+
|
61 |
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
|
62 |
+
{/* Left Column - Image */}
|
63 |
+
<div>
|
64 |
+
<div className="bg-gradient-to-br from-[#15151a] to-[#0c0c10] p-8 rounded-lg h-full flex items-center justify-center">
|
65 |
+
<img
|
66 |
+
src={vehicle.image}
|
67 |
+
alt={`${vehicle.name} ${vehicle.model}`}
|
68 |
+
className="w-full h-auto max-h-[400px] object-contain"
|
69 |
+
/>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
|
73 |
+
{/* Right Column - Info */}
|
74 |
+
<div>
|
75 |
+
<div className="mb-6">
|
76 |
+
<div className="flex items-start justify-between">
|
77 |
+
<div>
|
78 |
+
<h1 className="text-3xl md:text-4xl font-bold">
|
79 |
+
{vehicle.name} {vehicle.model}
|
80 |
+
</h1>
|
81 |
+
<div className="flex items-center gap-2 mt-2">
|
82 |
+
<span className="inline-block px-2.5 py-1 bg-blue-600 text-xs font-semibold rounded-full">
|
83 |
+
{vehicle.category}
|
84 |
+
</span>
|
85 |
+
<span className="text-gray-400">Electric</span>
|
86 |
+
</div>
|
87 |
+
</div>
|
88 |
+
<div className="text-right">
|
89 |
+
<div className="text-3xl font-bold">${vehicle.price}</div>
|
90 |
+
<div className="text-gray-400 text-sm">per day</div>
|
91 |
+
</div>
|
92 |
+
</div>
|
93 |
+
<p className="text-gray-300 mt-4">{vehicle.description}</p>
|
94 |
+
</div>
|
95 |
+
|
96 |
+
{/* Tabs */}
|
97 |
+
<div className="border-b border-gray-800 mb-6">
|
98 |
+
<nav className="flex gap-6">
|
99 |
+
<button
|
100 |
+
onClick={() => setActiveTab('overview')}
|
101 |
+
className={`pb-3 px-1 relative ${
|
102 |
+
activeTab === 'overview'
|
103 |
+
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0'
|
104 |
+
: 'text-gray-400 hover:text-white'
|
105 |
+
}`}
|
106 |
+
>
|
107 |
+
Overview
|
108 |
+
</button>
|
109 |
+
<button
|
110 |
+
onClick={() => setActiveTab('specs')}
|
111 |
+
className={`pb-3 px-1 relative ${
|
112 |
+
activeTab === 'specs'
|
113 |
+
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0'
|
114 |
+
: 'text-gray-400 hover:text-white'
|
115 |
+
}`}
|
116 |
+
>
|
117 |
+
Specifications
|
118 |
+
</button>
|
119 |
+
<button
|
120 |
+
onClick={() => setActiveTab('features')}
|
121 |
+
className={`pb-3 px-1 relative ${
|
122 |
+
activeTab === 'features'
|
123 |
+
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0'
|
124 |
+
: 'text-gray-400 hover:text-white'
|
125 |
+
}`}
|
126 |
+
>
|
127 |
+
Features
|
128 |
+
</button>
|
129 |
+
</nav>
|
130 |
+
</div>
|
131 |
+
|
132 |
+
{/* Tab Content */}
|
133 |
+
<div className="mb-8">
|
134 |
+
{activeTab === 'overview' && (
|
135 |
+
<div>
|
136 |
+
<div className="grid grid-cols-2 gap-4 mb-6">
|
137 |
+
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3">
|
138 |
+
<div className="bg-blue-600/20 p-2 rounded">
|
139 |
+
<Battery className="text-blue-500 w-5 h-5" />
|
140 |
+
</div>
|
141 |
+
<div>
|
142 |
+
<div className="text-sm text-gray-400">Range</div>
|
143 |
+
<div className="font-medium">{vehicle.specs.range}</div>
|
144 |
+
</div>
|
145 |
+
</div>
|
146 |
+
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3">
|
147 |
+
<div className="bg-blue-600/20 p-2 rounded">
|
148 |
+
<Zap className="text-blue-500 w-5 h-5" />
|
149 |
+
</div>
|
150 |
+
<div>
|
151 |
+
<div className="text-sm text-gray-400">Acceleration</div>
|
152 |
+
<div className="font-medium">{vehicle.specs.acceleration}</div>
|
153 |
+
</div>
|
154 |
+
</div>
|
155 |
+
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3">
|
156 |
+
<div className="bg-blue-600/20 p-2 rounded">
|
157 |
+
<Clock className="text-blue-500 w-5 h-5" />
|
158 |
+
</div>
|
159 |
+
<div>
|
160 |
+
<div className="text-sm text-gray-400">Top Speed</div>
|
161 |
+
<div className="font-medium">{vehicle.specs.topSpeed}</div>
|
162 |
+
</div>
|
163 |
+
</div>
|
164 |
+
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3">
|
165 |
+
<div className="bg-blue-600/20 p-2 rounded">
|
166 |
+
<Users className="text-blue-500 w-5 h-5" />
|
167 |
+
</div>
|
168 |
+
<div>
|
169 |
+
<div className="text-sm text-gray-400">Capacity</div>
|
170 |
+
<div className="font-medium">{vehicle.specs.capacity}</div>
|
171 |
+
</div>
|
172 |
+
</div>
|
173 |
+
</div>
|
174 |
+
|
175 |
+
<div className="mb-6">
|
176 |
+
<h3 className="text-lg font-medium mb-2">Rental Options</h3>
|
177 |
+
<div className="flex gap-4 mb-4">
|
178 |
+
<button
|
179 |
+
onClick={() => setRentalDuration(1)}
|
180 |
+
className={`flex-1 py-3 rounded-md transition-colors ${
|
181 |
+
rentalDuration === 1
|
182 |
+
? 'bg-blue-600 text-white'
|
183 |
+
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800'
|
184 |
+
}`}
|
185 |
+
>
|
186 |
+
1 Day
|
187 |
+
</button>
|
188 |
+
<button
|
189 |
+
onClick={() => setRentalDuration(3)}
|
190 |
+
className={`flex-1 py-3 rounded-md transition-colors ${
|
191 |
+
rentalDuration === 3
|
192 |
+
? 'bg-blue-600 text-white'
|
193 |
+
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800'
|
194 |
+
}`}
|
195 |
+
>
|
196 |
+
3 Days
|
197 |
+
</button>
|
198 |
+
<button
|
199 |
+
onClick={() => setRentalDuration(7)}
|
200 |
+
className={`flex-1 py-3 rounded-md transition-colors ${
|
201 |
+
rentalDuration === 7
|
202 |
+
? 'bg-blue-600 text-white'
|
203 |
+
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800'
|
204 |
+
}`}
|
205 |
+
>
|
206 |
+
7 Days
|
207 |
+
</button>
|
208 |
+
</div>
|
209 |
+
</div>
|
210 |
+
|
211 |
+
<div className="bg-[#15151a] p-5 rounded-lg mb-6">
|
212 |
+
<div className="flex justify-between mb-3">
|
213 |
+
<span className="text-gray-300">${vehicle.price} × {rentalDuration} day{rentalDuration > 1 ? 's' : ''}</span>
|
214 |
+
<span>${totalRentalPrice}</span>
|
215 |
+
</div>
|
216 |
+
<div className="flex justify-between mb-3">
|
217 |
+
<span className="text-gray-300">Insurance</span>
|
218 |
+
<span>Included</span>
|
219 |
+
</div>
|
220 |
+
<div className="flex justify-between mb-3">
|
221 |
+
<span className="text-gray-300">Charging</span>
|
222 |
+
<span>Included</span>
|
223 |
+
</div>
|
224 |
+
<div className="border-t border-gray-800 pt-3 mt-3 flex justify-between font-bold">
|
225 |
+
<span>Total</span>
|
226 |
+
<span>${totalRentalPrice}</span>
|
227 |
+
</div>
|
228 |
+
</div>
|
229 |
+
|
230 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
231 |
+
<button className="bg-blue-600 hover:bg-blue-700 p-4 rounded-md font-medium transition-colors flex items-center justify-center gap-2">
|
232 |
+
<Calendar size={18} />
|
233 |
+
Rent Now
|
234 |
+
</button>
|
235 |
+
<button className="bg-gray-800 hover:bg-gray-700 p-4 rounded-md font-medium transition-colors flex items-center justify-center gap-2">
|
236 |
+
<CreditCard size={18} />
|
237 |
+
Subscribe for ${vehicle.pricePerMonth}/mo
|
238 |
+
</button>
|
239 |
+
</div>
|
240 |
+
</div>
|
241 |
+
)}
|
242 |
+
|
243 |
+
{activeTab === 'specs' && (
|
244 |
+
<div>
|
245 |
+
<div className="bg-[#15151a] rounded-lg overflow-hidden">
|
246 |
+
<div className="grid grid-cols-1 md:grid-cols-2">
|
247 |
+
<div className="p-4 border-b md:border-b-0 md:border-r border-gray-800">
|
248 |
+
<h3 className="font-medium mb-3">Performance</h3>
|
249 |
+
<div className="space-y-3">
|
250 |
+
<div className="flex justify-between">
|
251 |
+
<span className="text-gray-400">Range</span>
|
252 |
+
<span>{vehicle.specs.range}</span>
|
253 |
+
</div>
|
254 |
+
<div className="flex justify-between">
|
255 |
+
<span className="text-gray-400">Top Speed</span>
|
256 |
+
<span>{vehicle.specs.topSpeed}</span>
|
257 |
+
</div>
|
258 |
+
<div className="flex justify-between">
|
259 |
+
<span className="text-gray-400">Acceleration</span>
|
260 |
+
<span>{vehicle.specs.acceleration}</span>
|
261 |
+
</div>
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
<div className="p-4">
|
265 |
+
<h3 className="font-medium mb-3">Interior</h3>
|
266 |
+
<div className="space-y-3">
|
267 |
+
<div className="flex justify-between">
|
268 |
+
<span className="text-gray-400">Seating</span>
|
269 |
+
<span>{vehicle.specs.capacity}</span>
|
270 |
+
</div>
|
271 |
+
<div className="flex justify-between">
|
272 |
+
<span className="text-gray-400">Touchscreen</span>
|
273 |
+
<span>15" Center Display</span>
|
274 |
+
</div>
|
275 |
+
<div className="flex justify-between">
|
276 |
+
<span className="text-gray-400">Sound System</span>
|
277 |
+
<span>Premium Audio</span>
|
278 |
+
</div>
|
279 |
+
</div>
|
280 |
+
</div>
|
281 |
+
</div>
|
282 |
+
<div className="border-t border-gray-800">
|
283 |
+
<div className="p-4">
|
284 |
+
<h3 className="font-medium mb-3">Technology</h3>
|
285 |
+
<div className="space-y-3">
|
286 |
+
<div className="flex justify-between">
|
287 |
+
<span className="text-gray-400">Autopilot</span>
|
288 |
+
<span>Included</span>
|
289 |
+
</div>
|
290 |
+
<div className="flex justify-between">
|
291 |
+
<span className="text-gray-400">Over-the-air Updates</span>
|
292 |
+
<span>Yes</span>
|
293 |
+
</div>
|
294 |
+
<div className="flex justify-between">
|
295 |
+
<span className="text-gray-400">Connectivity</span>
|
296 |
+
<span>Premium Connectivity</span>
|
297 |
+
</div>
|
298 |
+
</div>
|
299 |
+
</div>
|
300 |
+
</div>
|
301 |
+
</div>
|
302 |
+
</div>
|
303 |
+
)}
|
304 |
+
|
305 |
+
{activeTab === 'features' && (
|
306 |
+
<div>
|
307 |
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
308 |
+
{vehicle.features.map((feature, index) => (
|
309 |
+
<div key={index} className="flex items-center gap-2">
|
310 |
+
<div className="flex-shrink-0 w-5 h-5 rounded-full bg-green-600/20 flex items-center justify-center">
|
311 |
+
<Check className="text-green-500 w-3 h-3" />
|
312 |
+
</div>
|
313 |
+
<span>{feature}</span>
|
314 |
+
</div>
|
315 |
+
))}
|
316 |
+
</div>
|
317 |
+
</div>
|
318 |
+
)}
|
319 |
+
</div>
|
320 |
+
</div>
|
321 |
+
</div>
|
322 |
+
</div>
|
323 |
+
</div>
|
324 |
+
);
|
325 |
+
};
|
326 |
+
|
327 |
+
export default VehicleDetailPage;
|
VehiclesPage.tsx
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useEffect } from 'react';
|
2 |
+
import { vehicles, Vehicle } from '@/data/vehicles';
|
3 |
+
import VehicleCard from '@/components/ui/VehicleCard';
|
4 |
+
import { Search, Filter, X } from 'lucide-react';
|
5 |
+
|
6 |
+
const categories = ['All', 'Sedan', 'SUV', 'Truck'];
|
7 |
+
|
8 |
+
const VehiclesPage = () => {
|
9 |
+
const [searchQuery, setSearchQuery] = useState('');
|
10 |
+
const [selectedCategory, setSelectedCategory] = useState('All');
|
11 |
+
const [filteredVehicles, setFilteredVehicles] = useState<Vehicle[]>(vehicles);
|
12 |
+
const [isFilterOpen, setIsFilterOpen] = useState(false);
|
13 |
+
|
14 |
+
useEffect(() => {
|
15 |
+
filterVehicles();
|
16 |
+
}, [searchQuery, selectedCategory]);
|
17 |
+
|
18 |
+
const filterVehicles = () => {
|
19 |
+
let filtered = [...vehicles];
|
20 |
+
|
21 |
+
// Filter by search query
|
22 |
+
if (searchQuery.trim() !== '') {
|
23 |
+
const query = searchQuery.toLowerCase();
|
24 |
+
filtered = filtered.filter(
|
25 |
+
(vehicle) =>
|
26 |
+
vehicle.name.toLowerCase().includes(query) ||
|
27 |
+
vehicle.model.toLowerCase().includes(query) ||
|
28 |
+
vehicle.category.toLowerCase().includes(query)
|
29 |
+
);
|
30 |
+
}
|
31 |
+
|
32 |
+
// Filter by category
|
33 |
+
if (selectedCategory !== 'All') {
|
34 |
+
filtered = filtered.filter(
|
35 |
+
(vehicle) => vehicle.category === selectedCategory
|
36 |
+
);
|
37 |
+
}
|
38 |
+
|
39 |
+
setFilteredVehicles(filtered);
|
40 |
+
};
|
41 |
+
|
42 |
+
const handleClearSearch = () => {
|
43 |
+
setSearchQuery('');
|
44 |
+
};
|
45 |
+
|
46 |
+
const toggleFilter = () => {
|
47 |
+
setIsFilterOpen(!isFilterOpen);
|
48 |
+
};
|
49 |
+
|
50 |
+
return (
|
51 |
+
<div className="bg-[#0a0a0a] text-white min-h-screen">
|
52 |
+
<div className="container mx-auto px-4 py-12">
|
53 |
+
{/* Header */}
|
54 |
+
<div className="max-w-3xl mx-auto text-center mb-12">
|
55 |
+
<h1 className="text-4xl md:text-5xl font-bold mb-4">Our Electric Fleet</h1>
|
56 |
+
<p className="text-gray-300 text-lg">
|
57 |
+
Experience the future of transportation with our premium electric vehicles.
|
58 |
+
Zero emissions, maximum performance.
|
59 |
+
</p>
|
60 |
+
</div>
|
61 |
+
|
62 |
+
{/* Search and Filter */}
|
63 |
+
<div className="flex flex-col md:flex-row justify-between mb-8 gap-4">
|
64 |
+
<div className="relative flex-1">
|
65 |
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
66 |
+
<Search className="h-5 w-5 text-gray-400" />
|
67 |
+
</div>
|
68 |
+
<input
|
69 |
+
type="text"
|
70 |
+
value={searchQuery}
|
71 |
+
onChange={(e) => setSearchQuery(e.target.value)}
|
72 |
+
className="block w-full pl-10 pr-10 py-3 bg-[#161617] border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent text-white"
|
73 |
+
placeholder="Search vehicles..."
|
74 |
+
/>
|
75 |
+
{searchQuery && (
|
76 |
+
<button
|
77 |
+
onClick={handleClearSearch}
|
78 |
+
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-white"
|
79 |
+
>
|
80 |
+
<X className="h-5 w-5" />
|
81 |
+
</button>
|
82 |
+
)}
|
83 |
+
</div>
|
84 |
+
|
85 |
+
{/* Filter button for mobile */}
|
86 |
+
<button
|
87 |
+
onClick={toggleFilter}
|
88 |
+
className="md:hidden flex items-center justify-center gap-2 bg-[#161617] border border-gray-700 py-3 px-4 rounded-md"
|
89 |
+
>
|
90 |
+
<Filter className="h-5 w-5" />
|
91 |
+
<span>Filter</span>
|
92 |
+
</button>
|
93 |
+
|
94 |
+
{/* Filter options for desktop */}
|
95 |
+
<div className="hidden md:flex gap-2">
|
96 |
+
{categories.map((category) => (
|
97 |
+
<button
|
98 |
+
key={category}
|
99 |
+
onClick={() => setSelectedCategory(category)}
|
100 |
+
className={`px-4 py-2 rounded-md transition-colors ${
|
101 |
+
selectedCategory === category
|
102 |
+
? 'bg-blue-600 text-white'
|
103 |
+
: 'bg-[#161617] border border-gray-700 text-gray-300 hover:bg-gray-800'
|
104 |
+
}`}
|
105 |
+
>
|
106 |
+
{category}
|
107 |
+
</button>
|
108 |
+
))}
|
109 |
+
</div>
|
110 |
+
</div>
|
111 |
+
|
112 |
+
{/* Mobile filter dropdown */}
|
113 |
+
{isFilterOpen && (
|
114 |
+
<div className="md:hidden mb-6 bg-[#161617] border border-gray-700 rounded-md p-4">
|
115 |
+
<h3 className="font-medium mb-2">Filter by Category:</h3>
|
116 |
+
<div className="grid grid-cols-2 gap-2">
|
117 |
+
{categories.map((category) => (
|
118 |
+
<button
|
119 |
+
key={category}
|
120 |
+
onClick={() => {
|
121 |
+
setSelectedCategory(category);
|
122 |
+
setIsFilterOpen(false);
|
123 |
+
}}
|
124 |
+
className={`px-3 py-2 rounded-md text-sm transition-colors ${
|
125 |
+
selectedCategory === category
|
126 |
+
? 'bg-blue-600 text-white'
|
127 |
+
: 'bg-[#1a1a1c] text-gray-300 hover:bg-gray-800'
|
128 |
+
}`}
|
129 |
+
>
|
130 |
+
{category}
|
131 |
+
</button>
|
132 |
+
))}
|
133 |
+
</div>
|
134 |
+
</div>
|
135 |
+
)}
|
136 |
+
|
137 |
+
{/* Vehicles Grid */}
|
138 |
+
{filteredVehicles.length > 0 ? (
|
139 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
140 |
+
{filteredVehicles.map((vehicle) => (
|
141 |
+
<VehicleCard key={vehicle.id} vehicle={vehicle} />
|
142 |
+
))}
|
143 |
+
</div>
|
144 |
+
) : (
|
145 |
+
<div className="text-center py-12">
|
146 |
+
<h3 className="text-xl font-medium text-gray-400 mb-2">No vehicles found</h3>
|
147 |
+
<p className="text-gray-500">
|
148 |
+
Try adjusting your search or filter criteria.
|
149 |
+
</p>
|
150 |
+
</div>
|
151 |
+
)}
|
152 |
+
</div>
|
153 |
+
</div>
|
154 |
+
);
|
155 |
+
};
|
156 |
+
|
157 |
+
export default VehiclesPage;
|
bun.lock
ADDED
@@ -0,0 +1,546 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"lockfileVersion": 1,
|
3 |
+
"workspaces": {
|
4 |
+
"": {
|
5 |
+
"name": "aether-ride-vision",
|
6 |
+
"dependencies": {
|
7 |
+
"@headlessui/react": "^2.2.1",
|
8 |
+
"autoprefixer": "^10.4.21",
|
9 |
+
"lucide-react": "^0.487.0",
|
10 |
+
"postcss": "^8.5.3",
|
11 |
+
"react": "^19.0.0",
|
12 |
+
"react-dom": "^19.0.0",
|
13 |
+
"react-router-dom": "^7.5.0",
|
14 |
+
"tailwindcss": "^4.1.3",
|
15 |
+
},
|
16 |
+
"devDependencies": {
|
17 |
+
"@eslint/js": "^9.21.0",
|
18 |
+
"@types/react": "^19.0.10",
|
19 |
+
"@types/react-dom": "^19.0.4",
|
20 |
+
"@vitejs/plugin-react": "^4.3.4",
|
21 |
+
"eslint": "^9.21.0",
|
22 |
+
"eslint-plugin-react-hooks": "^5.1.0",
|
23 |
+
"eslint-plugin-react-refresh": "^0.4.19",
|
24 |
+
"globals": "^15.15.0",
|
25 |
+
"typescript": "~5.7.2",
|
26 |
+
"typescript-eslint": "^8.24.1",
|
27 |
+
"vite": "^6.2.0",
|
28 |
+
},
|
29 |
+
},
|
30 |
+
},
|
31 |
+
"packages": {
|
32 |
+
"@ampproject/remapping": ["@ampproject/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
33 |
+
|
34 |
+
"@babel/code-frame": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
|
35 |
+
|
36 |
+
"@babel/compat-data": ["@babel/[email protected]", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="],
|
37 |
+
|
38 |
+
"@babel/core": ["@babel/[email protected]", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.10", "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.10", "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ=="],
|
39 |
+
|
40 |
+
"@babel/generator": ["@babel/[email protected]", "", { "dependencies": { "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="],
|
41 |
+
|
42 |
+
"@babel/helper-compilation-targets": ["@babel/[email protected]", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA=="],
|
43 |
+
|
44 |
+
"@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
|
45 |
+
|
46 |
+
"@babel/helper-module-transforms": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="],
|
47 |
+
|
48 |
+
"@babel/helper-plugin-utils": ["@babel/[email protected]", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="],
|
49 |
+
|
50 |
+
"@babel/helper-string-parser": ["@babel/[email protected]", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
|
51 |
+
|
52 |
+
"@babel/helper-validator-identifier": ["@babel/[email protected]", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
53 |
+
|
54 |
+
"@babel/helper-validator-option": ["@babel/[email protected]", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="],
|
55 |
+
|
56 |
+
"@babel/helpers": ["@babel/[email protected]", "", { "dependencies": { "@babel/template": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg=="],
|
57 |
+
|
58 |
+
"@babel/parser": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="],
|
59 |
+
|
60 |
+
"@babel/plugin-transform-react-jsx-self": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg=="],
|
61 |
+
|
62 |
+
"@babel/plugin-transform-react-jsx-source": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg=="],
|
63 |
+
|
64 |
+
"@babel/template": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="],
|
65 |
+
|
66 |
+
"@babel/traverse": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.27.0", "@babel/parser": "^7.27.0", "@babel/template": "^7.27.0", "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="],
|
67 |
+
|
68 |
+
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="],
|
69 |
+
|
70 |
+
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="],
|
71 |
+
|
72 |
+
"@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="],
|
73 |
+
|
74 |
+
"@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="],
|
75 |
+
|
76 |
+
"@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="],
|
77 |
+
|
78 |
+
"@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="],
|
79 |
+
|
80 |
+
"@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="],
|
81 |
+
|
82 |
+
"@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="],
|
83 |
+
|
84 |
+
"@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="],
|
85 |
+
|
86 |
+
"@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="],
|
87 |
+
|
88 |
+
"@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="],
|
89 |
+
|
90 |
+
"@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="],
|
91 |
+
|
92 |
+
"@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="],
|
93 |
+
|
94 |
+
"@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="],
|
95 |
+
|
96 |
+
"@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="],
|
97 |
+
|
98 |
+
"@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="],
|
99 |
+
|
100 |
+
"@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="],
|
101 |
+
|
102 |
+
"@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="],
|
103 |
+
|
104 |
+
"@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="],
|
105 |
+
|
106 |
+
"@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="],
|
107 |
+
|
108 |
+
"@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="],
|
109 |
+
|
110 |
+
"@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="],
|
111 |
+
|
112 |
+
"@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="],
|
113 |
+
|
114 |
+
"@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="],
|
115 |
+
|
116 |
+
"@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="],
|
117 |
+
|
118 |
+
"@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="],
|
119 |
+
|
120 |
+
"@eslint-community/eslint-utils": ["@eslint-community/[email protected]", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="],
|
121 |
+
|
122 |
+
"@eslint-community/regexpp": ["@eslint-community/[email protected]", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
123 |
+
|
124 |
+
"@eslint/config-array": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
|
125 |
+
|
126 |
+
"@eslint/config-helpers": ["@eslint/[email protected]", "", {}, "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw=="],
|
127 |
+
|
128 |
+
"@eslint/core": ["@eslint/[email protected]", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="],
|
129 |
+
|
130 |
+
"@eslint/eslintrc": ["@eslint/[email protected]", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
131 |
+
|
132 |
+
"@eslint/js": ["@eslint/[email protected]", "", {}, "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA=="],
|
133 |
+
|
134 |
+
"@eslint/object-schema": ["@eslint/[email protected]", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
135 |
+
|
136 |
+
"@eslint/plugin-kit": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
|
137 |
+
|
138 |
+
"@floating-ui/core": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="],
|
139 |
+
|
140 |
+
"@floating-ui/dom": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="],
|
141 |
+
|
142 |
+
"@floating-ui/react": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw=="],
|
143 |
+
|
144 |
+
"@floating-ui/react-dom": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
145 |
+
|
146 |
+
"@floating-ui/utils": ["@floating-ui/[email protected]", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
|
147 |
+
|
148 |
+
"@headlessui/react": ["@headlessui/[email protected]", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.17.1", "@react-aria/interactions": "^3.21.3", "@tanstack/react-virtual": "^3.11.1" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-daiUqVLae8CKVjEVT19P/izW0aGK0GNhMSAeMlrDebKmoVZHcRRwbxzgtnEadUVDXyBsWo9/UH4KHeniO+0tMg=="],
|
149 |
+
|
150 |
+
"@humanfs/core": ["@humanfs/[email protected]", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
151 |
+
|
152 |
+
"@humanfs/node": ["@humanfs/[email protected]", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
153 |
+
|
154 |
+
"@humanwhocodes/module-importer": ["@humanwhocodes/[email protected]", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
155 |
+
|
156 |
+
"@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
|
157 |
+
|
158 |
+
"@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
159 |
+
|
160 |
+
"@jridgewell/resolve-uri": ["@jridgewell/[email protected]", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
161 |
+
|
162 |
+
"@jridgewell/set-array": ["@jridgewell/[email protected]", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
163 |
+
|
164 |
+
"@jridgewell/sourcemap-codec": ["@jridgewell/[email protected]", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
165 |
+
|
166 |
+
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
167 |
+
|
168 |
+
"@nodelib/fs.scandir": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
169 |
+
|
170 |
+
"@nodelib/fs.stat": ["@nodelib/[email protected]", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
171 |
+
|
172 |
+
"@nodelib/fs.walk": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
173 |
+
|
174 |
+
"@react-aria/focus": ["@react-aria/[email protected]", "", { "dependencies": { "@react-aria/interactions": "^3.24.1", "@react-aria/utils": "^3.28.1", "@react-types/shared": "^3.28.0", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-lgYs+sQ1TtBrAXnAdRBQrBo0/7o5H6IrfDxec1j+VRpcXL0xyk0xPq+m3lZp8typzIghqDgpnKkJ5Jf4OrzPIw=="],
|
175 |
+
|
176 |
+
"@react-aria/interactions": ["@react-aria/[email protected]", "", { "dependencies": { "@react-aria/ssr": "^3.9.7", "@react-aria/utils": "^3.28.1", "@react-stately/flags": "^3.1.0", "@react-types/shared": "^3.28.0", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-OWEcIC6UQfWq4Td5Ptuh4PZQ4LHLJr/JL2jGYvuNL6EgL3bWvzPrRYIF/R64YbfVxIC7FeZpPSkS07sZ93/NoA=="],
|
177 |
+
|
178 |
+
"@react-aria/ssr": ["@react-aria/[email protected]", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg=="],
|
179 |
+
|
180 |
+
"@react-aria/utils": ["@react-aria/[email protected]", "", { "dependencies": { "@react-aria/ssr": "^3.9.7", "@react-stately/flags": "^3.1.0", "@react-stately/utils": "^3.10.5", "@react-types/shared": "^3.28.0", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-mnHFF4YOVu9BRFQ1SZSKfPhg3z+lBRYoW5mLcYTQihbKhz48+I1sqRkP7ahMITr8ANH3nb34YaMME4XWmK2Mgg=="],
|
181 |
+
|
182 |
+
"@react-stately/flags": ["@react-stately/[email protected]", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-KSHOCxTFpBtxhIRcKwsD1YDTaNxFtCYuAUb0KEihc16QwqZViq4hasgPBs2gYm7fHRbw7WYzWKf6ZSo/+YsFlg=="],
|
183 |
+
|
184 |
+
"@react-stately/utils": ["@react-stately/[email protected]", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ=="],
|
185 |
+
|
186 |
+
"@react-types/shared": ["@react-types/[email protected]", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-9oMEYIDc3sk0G5rysnYvdNrkSg7B04yTKl50HHSZVbokeHpnU0yRmsDaWb9B/5RprcKj8XszEk5guBO8Sa/Q+Q=="],
|
187 |
+
|
188 |
+
"@rollup/rollup-android-arm-eabi": ["@rollup/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA=="],
|
189 |
+
|
190 |
+
"@rollup/rollup-android-arm64": ["@rollup/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ=="],
|
191 |
+
|
192 |
+
"@rollup/rollup-darwin-arm64": ["@rollup/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q=="],
|
193 |
+
|
194 |
+
"@rollup/rollup-darwin-x64": ["@rollup/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ=="],
|
195 |
+
|
196 |
+
"@rollup/rollup-freebsd-arm64": ["@rollup/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ=="],
|
197 |
+
|
198 |
+
"@rollup/rollup-freebsd-x64": ["@rollup/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q=="],
|
199 |
+
|
200 |
+
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g=="],
|
201 |
+
|
202 |
+
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw=="],
|
203 |
+
|
204 |
+
"@rollup/rollup-linux-arm64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ=="],
|
205 |
+
|
206 |
+
"@rollup/rollup-linux-arm64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA=="],
|
207 |
+
|
208 |
+
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw=="],
|
209 |
+
|
210 |
+
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ=="],
|
211 |
+
|
212 |
+
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ=="],
|
213 |
+
|
214 |
+
"@rollup/rollup-linux-riscv64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA=="],
|
215 |
+
|
216 |
+
"@rollup/rollup-linux-s390x-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA=="],
|
217 |
+
|
218 |
+
"@rollup/rollup-linux-x64-gnu": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA=="],
|
219 |
+
|
220 |
+
"@rollup/rollup-linux-x64-musl": ["@rollup/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg=="],
|
221 |
+
|
222 |
+
"@rollup/rollup-win32-arm64-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ=="],
|
223 |
+
|
224 |
+
"@rollup/rollup-win32-ia32-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ=="],
|
225 |
+
|
226 |
+
"@rollup/rollup-win32-x64-msvc": ["@rollup/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug=="],
|
227 |
+
|
228 |
+
"@swc/helpers": ["@swc/[email protected]", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
229 |
+
|
230 |
+
"@tanstack/react-virtual": ["@tanstack/[email protected]", "", { "dependencies": { "@tanstack/virtual-core": "3.13.6" }, "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" } }, "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA=="],
|
231 |
+
|
232 |
+
"@tanstack/virtual-core": ["@tanstack/[email protected]", "", {}, "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg=="],
|
233 |
+
|
234 |
+
"@types/babel__core": ["@types/[email protected]", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
235 |
+
|
236 |
+
"@types/babel__generator": ["@types/[email protected]", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
237 |
+
|
238 |
+
"@types/babel__template": ["@types/[email protected]", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
239 |
+
|
240 |
+
"@types/babel__traverse": ["@types/[email protected]", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
|
241 |
+
|
242 |
+
"@types/cookie": ["@types/[email protected]", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
243 |
+
|
244 |
+
"@types/estree": ["@types/[email protected]", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
245 |
+
|
246 |
+
"@types/json-schema": ["@types/[email protected]", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
247 |
+
|
248 |
+
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w=="],
|
249 |
+
|
250 |
+
"@types/react-dom": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w=="],
|
251 |
+
|
252 |
+
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/type-utils": "8.29.0", "@typescript-eslint/utils": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ=="],
|
253 |
+
|
254 |
+
"@typescript-eslint/parser": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", "@typescript-eslint/typescript-estree": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g=="],
|
255 |
+
|
256 |
+
"@typescript-eslint/scope-manager": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0" } }, "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw=="],
|
257 |
+
|
258 |
+
"@typescript-eslint/type-utils": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.29.0", "@typescript-eslint/utils": "8.29.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q=="],
|
259 |
+
|
260 |
+
"@typescript-eslint/types": ["@typescript-eslint/[email protected]", "", {}, "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg=="],
|
261 |
+
|
262 |
+
"@typescript-eslint/typescript-estree": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow=="],
|
263 |
+
|
264 |
+
"@typescript-eslint/utils": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", "@typescript-eslint/typescript-estree": "8.29.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA=="],
|
265 |
+
|
266 |
+
"@typescript-eslint/visitor-keys": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg=="],
|
267 |
+
|
268 |
+
"@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="],
|
269 |
+
|
270 |
+
"acorn": ["[email protected]", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
271 |
+
|
272 |
+
"acorn-jsx": ["[email protected]", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
273 |
+
|
274 |
+
"ajv": ["[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
275 |
+
|
276 |
+
"ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
277 |
+
|
278 |
+
"argparse": ["[email protected]", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
279 |
+
|
280 |
+
"autoprefixer": ["[email protected]", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
|
281 |
+
|
282 |
+
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
283 |
+
|
284 |
+
"brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
285 |
+
|
286 |
+
"braces": ["[email protected]", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
287 |
+
|
288 |
+
"browserslist": ["[email protected]", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
|
289 |
+
|
290 |
+
"callsites": ["[email protected]", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
291 |
+
|
292 |
+
"caniuse-lite": ["[email protected]", "", {}, "sha512-B5C0I0UmaGqHgo5FuqJ7hBd4L57A4dDD+Xi+XX1nXOoxGeDdY4Ko38qJYOyqznBVJEqON5p8P1x5zRR3+rsnxA=="],
|
293 |
+
|
294 |
+
"chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
295 |
+
|
296 |
+
"clsx": ["[email protected]", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
297 |
+
|
298 |
+
"color-convert": ["[email protected]", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
299 |
+
|
300 |
+
"color-name": ["[email protected]", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
301 |
+
|
302 |
+
"concat-map": ["[email protected]", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
303 |
+
|
304 |
+
"convert-source-map": ["[email protected]", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
305 |
+
|
306 |
+
"cookie": ["[email protected]", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
307 |
+
|
308 |
+
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
309 |
+
|
310 |
+
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
311 |
+
|
312 |
+
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
313 |
+
|
314 |
+
"deep-is": ["[email protected]", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
315 |
+
|
316 |
+
"electron-to-chromium": ["[email protected]", "", {}, "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg=="],
|
317 |
+
|
318 |
+
"esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="],
|
319 |
+
|
320 |
+
"escalade": ["[email protected]", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
321 |
+
|
322 |
+
"escape-string-regexp": ["[email protected]", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
323 |
+
|
324 |
+
"eslint": ["[email protected]", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw=="],
|
325 |
+
|
326 |
+
"eslint-plugin-react-hooks": ["[email protected]", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
|
327 |
+
|
328 |
+
"eslint-plugin-react-refresh": ["[email protected]", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ=="],
|
329 |
+
|
330 |
+
"eslint-scope": ["[email protected]", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
|
331 |
+
|
332 |
+
"eslint-visitor-keys": ["[email protected]", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
|
333 |
+
|
334 |
+
"espree": ["[email protected]", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
|
335 |
+
|
336 |
+
"esquery": ["[email protected]", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
337 |
+
|
338 |
+
"esrecurse": ["[email protected]", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
339 |
+
|
340 |
+
"estraverse": ["[email protected]", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
341 |
+
|
342 |
+
"esutils": ["[email protected]", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
343 |
+
|
344 |
+
"fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
345 |
+
|
346 |
+
"fast-glob": ["[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
347 |
+
|
348 |
+
"fast-json-stable-stringify": ["[email protected]", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
349 |
+
|
350 |
+
"fast-levenshtein": ["[email protected]", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
351 |
+
|
352 |
+
"fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
353 |
+
|
354 |
+
"file-entry-cache": ["[email protected]", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
355 |
+
|
356 |
+
"fill-range": ["[email protected]", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
357 |
+
|
358 |
+
"find-up": ["[email protected]", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
359 |
+
|
360 |
+
"flat-cache": ["[email protected]", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
361 |
+
|
362 |
+
"flatted": ["[email protected]", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
363 |
+
|
364 |
+
"fraction.js": ["[email protected]", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
365 |
+
|
366 |
+
"fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
367 |
+
|
368 |
+
"gensync": ["[email protected]", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
369 |
+
|
370 |
+
"glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
371 |
+
|
372 |
+
"globals": ["[email protected]", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
|
373 |
+
|
374 |
+
"graphemer": ["[email protected]", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
375 |
+
|
376 |
+
"has-flag": ["[email protected]", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
377 |
+
|
378 |
+
"ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
379 |
+
|
380 |
+
"import-fresh": ["[email protected]", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
381 |
+
|
382 |
+
"imurmurhash": ["[email protected]", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
383 |
+
|
384 |
+
"is-extglob": ["[email protected]", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
385 |
+
|
386 |
+
"is-glob": ["[email protected]", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
387 |
+
|
388 |
+
"is-number": ["[email protected]", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
389 |
+
|
390 |
+
"isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
391 |
+
|
392 |
+
"js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
393 |
+
|
394 |
+
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
395 |
+
|
396 |
+
"jsesc": ["[email protected]", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
397 |
+
|
398 |
+
"json-buffer": ["[email protected]", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
399 |
+
|
400 |
+
"json-schema-traverse": ["[email protected]", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
401 |
+
|
402 |
+
"json-stable-stringify-without-jsonify": ["[email protected]", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
403 |
+
|
404 |
+
"json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
405 |
+
|
406 |
+
"keyv": ["[email protected]", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
407 |
+
|
408 |
+
"levn": ["[email protected]", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
409 |
+
|
410 |
+
"locate-path": ["[email protected]", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
411 |
+
|
412 |
+
"lodash.merge": ["[email protected]", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
413 |
+
|
414 |
+
"lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
415 |
+
|
416 |
+
"lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw=="],
|
417 |
+
|
418 |
+
"merge2": ["[email protected]", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
419 |
+
|
420 |
+
"micromatch": ["[email protected]", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
421 |
+
|
422 |
+
"minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
423 |
+
|
424 |
+
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
425 |
+
|
426 |
+
"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
427 |
+
|
428 |
+
"natural-compare": ["[email protected]", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
429 |
+
|
430 |
+
"node-releases": ["[email protected]", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
|
431 |
+
|
432 |
+
"normalize-range": ["[email protected]", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
|
433 |
+
|
434 |
+
"optionator": ["[email protected]", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
435 |
+
|
436 |
+
"p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
437 |
+
|
438 |
+
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
439 |
+
|
440 |
+
"parent-module": ["[email protected]", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
441 |
+
|
442 |
+
"path-exists": ["[email protected]", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
443 |
+
|
444 |
+
"path-key": ["[email protected]", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
445 |
+
|
446 |
+
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
447 |
+
|
448 |
+
"picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
449 |
+
|
450 |
+
"postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
451 |
+
|
452 |
+
"postcss-value-parser": ["[email protected]", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
453 |
+
|
454 |
+
"prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
455 |
+
|
456 |
+
"punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
457 |
+
|
458 |
+
"queue-microtask": ["[email protected]", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
459 |
+
|
460 |
+
"react": ["[email protected]", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
461 |
+
|
462 |
+
"react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
463 |
+
|
464 |
+
"react-refresh": ["[email protected]", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
|
465 |
+
|
466 |
+
"react-router": ["[email protected]", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0", "turbo-stream": "2.4.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g=="],
|
467 |
+
|
468 |
+
"react-router-dom": ["[email protected]", "", { "dependencies": { "react-router": "7.5.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA=="],
|
469 |
+
|
470 |
+
"resolve-from": ["[email protected]", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
471 |
+
|
472 |
+
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
473 |
+
|
474 |
+
"rollup": ["[email protected]", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.39.0", "@rollup/rollup-android-arm64": "4.39.0", "@rollup/rollup-darwin-arm64": "4.39.0", "@rollup/rollup-darwin-x64": "4.39.0", "@rollup/rollup-freebsd-arm64": "4.39.0", "@rollup/rollup-freebsd-x64": "4.39.0", "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", "@rollup/rollup-linux-arm-musleabihf": "4.39.0", "@rollup/rollup-linux-arm64-gnu": "4.39.0", "@rollup/rollup-linux-arm64-musl": "4.39.0", "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", "@rollup/rollup-linux-riscv64-gnu": "4.39.0", "@rollup/rollup-linux-riscv64-musl": "4.39.0", "@rollup/rollup-linux-s390x-gnu": "4.39.0", "@rollup/rollup-linux-x64-gnu": "4.39.0", "@rollup/rollup-linux-x64-musl": "4.39.0", "@rollup/rollup-win32-arm64-msvc": "4.39.0", "@rollup/rollup-win32-ia32-msvc": "4.39.0", "@rollup/rollup-win32-x64-msvc": "4.39.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g=="],
|
475 |
+
|
476 |
+
"run-parallel": ["[email protected]", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
477 |
+
|
478 |
+
"scheduler": ["[email protected]", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
479 |
+
|
480 |
+
"semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
481 |
+
|
482 |
+
"set-cookie-parser": ["[email protected]", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
|
483 |
+
|
484 |
+
"shebang-command": ["[email protected]", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
485 |
+
|
486 |
+
"shebang-regex": ["[email protected]", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
487 |
+
|
488 |
+
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
489 |
+
|
490 |
+
"strip-json-comments": ["[email protected]", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
491 |
+
|
492 |
+
"supports-color": ["[email protected]", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
493 |
+
|
494 |
+
"tabbable": ["[email protected]", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
495 |
+
|
496 |
+
"tailwindcss": ["[email protected]", "", {}, "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g=="],
|
497 |
+
|
498 |
+
"to-regex-range": ["[email protected]", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
499 |
+
|
500 |
+
"ts-api-utils": ["[email protected]", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
501 |
+
|
502 |
+
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
503 |
+
|
504 |
+
"turbo-stream": ["[email protected]", "", {}, "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g=="],
|
505 |
+
|
506 |
+
"type-check": ["[email protected]", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
507 |
+
|
508 |
+
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
509 |
+
|
510 |
+
"typescript-eslint": ["[email protected]", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.29.0", "@typescript-eslint/parser": "8.29.0", "@typescript-eslint/utils": "8.29.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg=="],
|
511 |
+
|
512 |
+
"update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
513 |
+
|
514 |
+
"uri-js": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
515 |
+
|
516 |
+
"vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA=="],
|
517 |
+
|
518 |
+
"which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
519 |
+
|
520 |
+
"word-wrap": ["[email protected]", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
521 |
+
|
522 |
+
"yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
523 |
+
|
524 |
+
"yocto-queue": ["[email protected]", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
525 |
+
|
526 |
+
"@babel/traverse/globals": ["[email protected]", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
|
527 |
+
|
528 |
+
"@eslint-community/eslint-utils/eslint-visitor-keys": ["[email protected]", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
529 |
+
|
530 |
+
"@eslint/eslintrc/globals": ["[email protected]", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
531 |
+
|
532 |
+
"@eslint/plugin-kit/@eslint/core": ["@eslint/[email protected]", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw=="],
|
533 |
+
|
534 |
+
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
535 |
+
|
536 |
+
"@typescript-eslint/typescript-estree/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
537 |
+
|
538 |
+
"@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
539 |
+
|
540 |
+
"eslint/@eslint/js": ["@eslint/[email protected]", "", {}, "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw=="],
|
541 |
+
|
542 |
+
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
543 |
+
|
544 |
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
545 |
+
}
|
546 |
+
}
|
eslint.config.js
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import js from '@eslint/js'
|
2 |
+
import globals from 'globals'
|
3 |
+
import reactHooks from 'eslint-plugin-react-hooks'
|
4 |
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
5 |
+
import tseslint from 'typescript-eslint'
|
6 |
+
|
7 |
+
export default tseslint.config(
|
8 |
+
{ ignores: ['dist'] },
|
9 |
+
{
|
10 |
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
11 |
+
files: ['**/*.{ts,tsx}'],
|
12 |
+
languageOptions: {
|
13 |
+
ecmaVersion: 2020,
|
14 |
+
globals: globals.browser,
|
15 |
+
},
|
16 |
+
plugins: {
|
17 |
+
'react-hooks': reactHooks,
|
18 |
+
'react-refresh': reactRefresh,
|
19 |
+
},
|
20 |
+
rules: {
|
21 |
+
...reactHooks.configs.recommended.rules,
|
22 |
+
'react-refresh/only-export-components': [
|
23 |
+
'warn',
|
24 |
+
{ allowConstantExport: true },
|
25 |
+
],
|
26 |
+
},
|
27 |
+
},
|
28 |
+
)
|
index.css
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
4 |
+
|
5 |
+
:root {
|
6 |
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
7 |
+
line-height: 1.5;
|
8 |
+
font-weight: 400;
|
9 |
+
|
10 |
+
color-scheme: light dark;
|
11 |
+
color: rgba(255, 255, 255, 0.87);
|
12 |
+
background-color: #242424;
|
13 |
+
|
14 |
+
font-synthesis: none;
|
15 |
+
text-rendering: optimizeLegibility;
|
16 |
+
-webkit-font-smoothing: antialiased;
|
17 |
+
-moz-osx-font-smoothing: grayscale;
|
18 |
+
|
19 |
+
color-scheme: dark;
|
20 |
+
--bg-color: #0a0a0a;
|
21 |
+
--text-color: #f0f0f0;
|
22 |
+
--primary-color: #147dec;
|
23 |
+
--secondary-color: #29a2c1;
|
24 |
+
}
|
25 |
+
|
26 |
+
a {
|
27 |
+
font-weight: 500;
|
28 |
+
color: #646cff;
|
29 |
+
text-decoration: inherit;
|
30 |
+
}
|
31 |
+
a:hover {
|
32 |
+
color: #535bf2;
|
33 |
+
}
|
34 |
+
|
35 |
+
body {
|
36 |
+
margin: 0;
|
37 |
+
display: flex;
|
38 |
+
place-items: center;
|
39 |
+
min-width: 320px;
|
40 |
+
min-height: 100vh;
|
41 |
+
background-color: var(--bg-color);
|
42 |
+
color: var(--text-color);
|
43 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
44 |
+
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
45 |
+
padding: 0;
|
46 |
+
}
|
47 |
+
|
48 |
+
h1 {
|
49 |
+
font-size: 3.2em;
|
50 |
+
line-height: 1.1;
|
51 |
+
}
|
52 |
+
|
53 |
+
button {
|
54 |
+
border-radius: 8px;
|
55 |
+
border: 1px solid transparent;
|
56 |
+
padding: 0.6em 1.2em;
|
57 |
+
font-size: 1em;
|
58 |
+
font-weight: 500;
|
59 |
+
font-family: inherit;
|
60 |
+
background-color: #1a1a1a;
|
61 |
+
cursor: pointer;
|
62 |
+
transition: border-color 0.25s;
|
63 |
+
}
|
64 |
+
button:hover {
|
65 |
+
border-color: #646cff;
|
66 |
+
}
|
67 |
+
button:focus,
|
68 |
+
button:focus-visible {
|
69 |
+
outline: 4px auto -webkit-focus-ring-color;
|
70 |
+
}
|
71 |
+
|
72 |
+
@layer components {
|
73 |
+
.container {
|
74 |
+
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
|
75 |
+
}
|
76 |
+
|
77 |
+
.btn-primary {
|
78 |
+
@apply bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md transition-colors;
|
79 |
+
}
|
80 |
+
|
81 |
+
.btn-secondary {
|
82 |
+
@apply bg-transparent border border-blue-600 text-blue-600 hover:bg-blue-600/10 px-4 py-2 rounded-md transition-colors;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
@media (prefers-color-scheme: light) {
|
87 |
+
:root {
|
88 |
+
color: #213547;
|
89 |
+
background-color: #ffffff;
|
90 |
+
}
|
91 |
+
a:hover {
|
92 |
+
color: #747bff;
|
93 |
+
}
|
94 |
+
button {
|
95 |
+
background-color: #f9f9f9;
|
96 |
+
}
|
97 |
+
}
|
index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Vite + React + TS</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="root"></div>
|
11 |
+
<script type="module" src="/src/main.tsx"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
main.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import ReactDOM from 'react-dom/client';
|
3 |
+
import './index.css';
|
4 |
+
import App from './App.tsx';
|
5 |
+
|
6 |
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
7 |
+
<React.StrictMode>
|
8 |
+
<App />
|
9 |
+
</React.StrictMode>,
|
10 |
+
);
|
package.json
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "aether-ride-vision",
|
3 |
+
"private": true,
|
4 |
+
"version": "0.0.0",
|
5 |
+
"type": "module",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "vite --host 0.0.0.0",
|
8 |
+
"build": "tsc && vite build",
|
9 |
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
10 |
+
"preview": "vite preview"
|
11 |
+
},
|
12 |
+
"dependencies": {
|
13 |
+
"@headlessui/react": "^2.2.1",
|
14 |
+
"autoprefixer": "^10.4.21",
|
15 |
+
"lucide-react": "^0.487.0",
|
16 |
+
"postcss": "^8.5.3",
|
17 |
+
"react": "^19.0.0",
|
18 |
+
"react-dom": "^19.0.0",
|
19 |
+
"react-router-dom": "^7.5.0",
|
20 |
+
"tailwindcss": "^4.1.3"
|
21 |
+
},
|
22 |
+
"devDependencies": {
|
23 |
+
"@eslint/js": "^9.21.0",
|
24 |
+
"@types/react": "^19.0.10",
|
25 |
+
"@types/react-dom": "^19.0.4",
|
26 |
+
"@vitejs/plugin-react": "^4.3.4",
|
27 |
+
"eslint": "^9.21.0",
|
28 |
+
"eslint-plugin-react-hooks": "^5.1.0",
|
29 |
+
"eslint-plugin-react-refresh": "^0.4.19",
|
30 |
+
"globals": "^15.15.0",
|
31 |
+
"typescript": "~5.7.2",
|
32 |
+
"typescript-eslint": "^8.24.1",
|
33 |
+
"vite": "^6.2.0"
|
34 |
+
}
|
35 |
+
}
|
postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
react.svg
ADDED
|
tailwind.config.js
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: [
|
4 |
+
"./index.html",
|
5 |
+
"./src/**/*.{js,ts,jsx,tsx}",
|
6 |
+
],
|
7 |
+
theme: {
|
8 |
+
extend: {},
|
9 |
+
},
|
10 |
+
plugins: [],
|
11 |
+
}
|
tesla-cybertruck.png
ADDED
![]() |
tesla-model-3.png
ADDED
![]() |
tesla-model-s.png
ADDED
![]() |
tesla-model-x.png
ADDED
![]() |
Git LFS Details
|
tesla-model-y.png
ADDED
![]() |
Git LFS Details
|
tsconfig.app.json
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
4 |
+
"target": "ES2020",
|
5 |
+
"useDefineForClassFields": true,
|
6 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
7 |
+
"module": "ESNext",
|
8 |
+
"skipLibCheck": true,
|
9 |
+
|
10 |
+
/* Bundler mode */
|
11 |
+
"moduleResolution": "bundler",
|
12 |
+
"allowImportingTsExtensions": true,
|
13 |
+
"isolatedModules": true,
|
14 |
+
"moduleDetection": "force",
|
15 |
+
"noEmit": true,
|
16 |
+
"jsx": "react-jsx",
|
17 |
+
|
18 |
+
/* Linting */
|
19 |
+
"strict": true,
|
20 |
+
"noUnusedLocals": true,
|
21 |
+
"noUnusedParameters": true,
|
22 |
+
"noFallthroughCasesInSwitch": true,
|
23 |
+
"noUncheckedSideEffectImports": true
|
24 |
+
},
|
25 |
+
"include": ["src"]
|
26 |
+
}
|
tsconfig.json
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"files": [],
|
3 |
+
"references": [
|
4 |
+
{ "path": "./tsconfig.app.json" },
|
5 |
+
{ "path": "./tsconfig.node.json" }
|
6 |
+
]
|
7 |
+
}
|
tsconfig.node.json
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
4 |
+
"target": "ES2022",
|
5 |
+
"lib": ["ES2023"],
|
6 |
+
"module": "ESNext",
|
7 |
+
"skipLibCheck": true,
|
8 |
+
|
9 |
+
/* Bundler mode */
|
10 |
+
"moduleResolution": "bundler",
|
11 |
+
"allowImportingTsExtensions": true,
|
12 |
+
"isolatedModules": true,
|
13 |
+
"moduleDetection": "force",
|
14 |
+
"noEmit": true,
|
15 |
+
|
16 |
+
/* Linting */
|
17 |
+
"strict": true,
|
18 |
+
"noUnusedLocals": true,
|
19 |
+
"noUnusedParameters": true,
|
20 |
+
"noFallthroughCasesInSwitch": true,
|
21 |
+
"noUncheckedSideEffectImports": true
|
22 |
+
},
|
23 |
+
"include": ["vite.config.ts"]
|
24 |
+
}
|
vehicles.ts
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import teslaModelS from '@/assets/cars/tesla-model-s.png';
|
2 |
+
import teslaModel3 from '@/assets/cars/tesla-model-3.png';
|
3 |
+
import teslaModelX from '@/assets/cars/tesla-model-x.png';
|
4 |
+
import teslaModelY from '@/assets/cars/tesla-model-y.png';
|
5 |
+
import teslaCybertruck from '@/assets/cars/tesla-cybertruck.png';
|
6 |
+
|
7 |
+
export interface Vehicle {
|
8 |
+
id: string;
|
9 |
+
name: string;
|
10 |
+
model: string;
|
11 |
+
category: string;
|
12 |
+
price: number;
|
13 |
+
pricePerMonth: number;
|
14 |
+
image: string;
|
15 |
+
description: string;
|
16 |
+
specs: {
|
17 |
+
range: string;
|
18 |
+
topSpeed: string;
|
19 |
+
acceleration: string;
|
20 |
+
capacity: string;
|
21 |
+
};
|
22 |
+
features: string[];
|
23 |
+
}
|
24 |
+
|
25 |
+
export const vehicles: Vehicle[] = [
|
26 |
+
{
|
27 |
+
id: 'model-s',
|
28 |
+
name: 'Tesla',
|
29 |
+
model: 'Model S',
|
30 |
+
category: 'Sedan',
|
31 |
+
price: 249,
|
32 |
+
pricePerMonth: 1799,
|
33 |
+
image: teslaModelS,
|
34 |
+
description: 'Experience unparalleled performance with the Tesla Model S. With its sleek design, long range, and advanced autopilot capabilities, the Model S redefines what a car can be.',
|
35 |
+
specs: {
|
36 |
+
range: '405 miles',
|
37 |
+
topSpeed: '200 mph',
|
38 |
+
acceleration: '1.99s 0-60 mph',
|
39 |
+
capacity: '5 passengers',
|
40 |
+
},
|
41 |
+
features: [
|
42 |
+
'Autopilot',
|
43 |
+
'Full Self-Driving Capability',
|
44 |
+
'Premium Interior',
|
45 |
+
'Wireless Charging',
|
46 |
+
'Premium Audio System',
|
47 |
+
'Heated Seats',
|
48 |
+
'Glass Roof',
|
49 |
+
],
|
50 |
+
},
|
51 |
+
{
|
52 |
+
id: 'model-3',
|
53 |
+
name: 'Tesla',
|
54 |
+
model: 'Model 3',
|
55 |
+
category: 'Sedan',
|
56 |
+
price: 169,
|
57 |
+
pricePerMonth: 1099,
|
58 |
+
image: teslaModel3,
|
59 |
+
description: 'The Tesla Model 3 is an all-electric fastback that combines affordability with advanced technology and impressive range.',
|
60 |
+
specs: {
|
61 |
+
range: '358 miles',
|
62 |
+
topSpeed: '162 mph',
|
63 |
+
acceleration: '3.1s 0-60 mph',
|
64 |
+
capacity: '5 passengers',
|
65 |
+
},
|
66 |
+
features: [
|
67 |
+
'Autopilot',
|
68 |
+
'Full Self-Driving Capability',
|
69 |
+
'Minimalist Interior',
|
70 |
+
'Wireless Charging',
|
71 |
+
'Premium Audio System',
|
72 |
+
'Heated Seats',
|
73 |
+
'Glass Roof',
|
74 |
+
],
|
75 |
+
},
|
76 |
+
{
|
77 |
+
id: 'model-x',
|
78 |
+
name: 'Tesla',
|
79 |
+
model: 'Model X',
|
80 |
+
category: 'SUV',
|
81 |
+
price: 299,
|
82 |
+
pricePerMonth: 2199,
|
83 |
+
image: teslaModelX,
|
84 |
+
description: 'The Tesla Model X is an all-electric luxury SUV with falcon-wing doors, spacious interior, and impressive performance.',
|
85 |
+
specs: {
|
86 |
+
range: '371 miles',
|
87 |
+
topSpeed: '163 mph',
|
88 |
+
acceleration: '2.5s 0-60 mph',
|
89 |
+
capacity: '7 passengers',
|
90 |
+
},
|
91 |
+
features: [
|
92 |
+
'Falcon Wing Doors',
|
93 |
+
'Autopilot',
|
94 |
+
'Full Self-Driving Capability',
|
95 |
+
'Premium Interior',
|
96 |
+
'Wireless Charging',
|
97 |
+
'Premium Audio System',
|
98 |
+
'Heated Seats',
|
99 |
+
'Panoramic Windshield',
|
100 |
+
],
|
101 |
+
},
|
102 |
+
{
|
103 |
+
id: 'model-y',
|
104 |
+
name: 'Tesla',
|
105 |
+
model: 'Model Y',
|
106 |
+
category: 'SUV',
|
107 |
+
price: 199,
|
108 |
+
pricePerMonth: 1499,
|
109 |
+
image: teslaModelY,
|
110 |
+
description: 'The Tesla Model Y is a compact SUV built on the Model 3 platform with added versatility and space.',
|
111 |
+
specs: {
|
112 |
+
range: '330 miles',
|
113 |
+
topSpeed: '155 mph',
|
114 |
+
acceleration: '3.5s 0-60 mph',
|
115 |
+
capacity: '7 passengers',
|
116 |
+
},
|
117 |
+
features: [
|
118 |
+
'Autopilot',
|
119 |
+
'Full Self-Driving Capability',
|
120 |
+
'Minimalist Interior',
|
121 |
+
'Wireless Charging',
|
122 |
+
'Premium Audio System',
|
123 |
+
'Heated Seats',
|
124 |
+
'Glass Roof',
|
125 |
+
'Spacious Cargo Area',
|
126 |
+
],
|
127 |
+
},
|
128 |
+
{
|
129 |
+
id: 'cybertruck',
|
130 |
+
name: 'Tesla',
|
131 |
+
model: 'Cybertruck',
|
132 |
+
category: 'Truck',
|
133 |
+
price: 349,
|
134 |
+
pricePerMonth: 2499,
|
135 |
+
image: teslaCybertruck,
|
136 |
+
description: 'The Tesla Cybertruck is an all-electric, battery-powered, light-duty truck with a futuristic angular design and impressive capability.',
|
137 |
+
specs: {
|
138 |
+
range: '500+ miles',
|
139 |
+
topSpeed: '130 mph',
|
140 |
+
acceleration: '2.9s 0-60 mph',
|
141 |
+
capacity: '6 passengers',
|
142 |
+
},
|
143 |
+
features: [
|
144 |
+
'Ultra-Hard 30X Cold-Rolled Stainless Steel',
|
145 |
+
'Armor Glass',
|
146 |
+
'Adjustable Air Suspension',
|
147 |
+
'Autopilot',
|
148 |
+
'Full Self-Driving Capability',
|
149 |
+
'Vault-Like Storage',
|
150 |
+
'14,000+ lbs Towing Capacity',
|
151 |
+
'Onboard Power Inverter',
|
152 |
+
],
|
153 |
+
},
|
154 |
+
];
|
155 |
+
|
156 |
+
export const getVehicleById = (id: string): Vehicle | undefined => {
|
157 |
+
return vehicles.find(vehicle => vehicle.id === id);
|
158 |
+
};
|
159 |
+
|
160 |
+
export const getVehiclesByCategory = (category: string): Vehicle[] => {
|
161 |
+
return vehicles.filter(vehicle => vehicle.category === category);
|
162 |
+
};
|
vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
vite.config.ts
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react'
|
3 |
+
import path from 'path'
|
4 |
+
|
5 |
+
// https://vite.dev/config/
|
6 |
+
export default defineConfig({
|
7 |
+
plugins: [react()],
|
8 |
+
resolve: {
|
9 |
+
alias: {
|
10 |
+
'@': path.resolve(__dirname, './src'),
|
11 |
+
},
|
12 |
+
},
|
13 |
+
server: {
|
14 |
+
host: '0.0.0.0',
|
15 |
+
port: 5173,
|
16 |
+
},
|
17 |
+
})
|
vite.svg
ADDED
|