optimization
This commit is contained in:
@@ -5,6 +5,14 @@ export default function Document() {
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<meta charSet="utf-8" />
|
||||
{/* Font optimization */}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
{/* Audio preload */}
|
||||
<link rel="preload" href="/notification.mp3" as="audio" type="audio/mpeg" />
|
||||
</Head>
|
||||
<body>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Search, Filter, Eye, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import OrderDetailsModal from "./OrderDetailsModal";
|
||||
|
||||
interface Order {
|
||||
@@ -57,30 +58,85 @@ const getStatusStyle = (status: string) => {
|
||||
* This component should only be used in admin contexts
|
||||
*/
|
||||
export default function OrdersTable({ orders, enableModal = true }: OrdersTableProps) {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("all");
|
||||
const [selectedOrderId, setSelectedOrderId] = useState<number | string | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [selectedOrderId, setSelectedOrderId] = useState<string | number | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const itemsPerPage = 10;
|
||||
|
||||
const itemsPerPage = 20;
|
||||
|
||||
// Filter orders based on search and status
|
||||
const filteredOrders = orders.filter(order => {
|
||||
const matchesSearch = order.orderId.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
order.userId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(order.vendorUsername && order.vendorUsername.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
const filteredOrders = useMemo(() => {
|
||||
return orders.filter((order) => {
|
||||
const matchesSearch = searchTerm === "" ||
|
||||
order.orderId.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
order.userId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(order.vendorUsername && order.vendorUsername.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
|
||||
const matchesStatus = statusFilter === "all" || order.status === statusFilter;
|
||||
const matchesStatus = statusFilter === "all" || order.status === statusFilter;
|
||||
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
}, [orders, searchTerm, statusFilter]);
|
||||
|
||||
// Calculate pagination
|
||||
// Pagination calculations
|
||||
const totalPages = Math.ceil(filteredOrders.length / itemsPerPage);
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
const currentOrders = filteredOrders.slice(startIndex, endIndex);
|
||||
|
||||
// Virtual scrolling row renderer
|
||||
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => {
|
||||
const order = currentOrders[index];
|
||||
if (!order) return null;
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<TableRow>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Determine if we should use virtual scrolling (for performance with large datasets)
|
||||
const useVirtualScrolling = currentOrders.length > 50;
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page);
|
||||
};
|
||||
@@ -152,47 +208,58 @@ export default function OrdersTable({ orders, enableModal = true }: OrdersTableP
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{currentOrders.map((order) => (
|
||||
<TableRow key={order.orderId}>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
{useVirtualScrolling ? (
|
||||
<List
|
||||
height={400}
|
||||
itemCount={currentOrders.length}
|
||||
itemSize={60}
|
||||
className="border"
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
) : (
|
||||
<TableBody>
|
||||
{currentOrders.map((order) => (
|
||||
<TableRow key={order.orderId}>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
|
||||
{/* Pagination */}
|
||||
|
||||
@@ -47,7 +47,8 @@ import { DateRange } from "react-day-picker";
|
||||
import { addDays, startOfDay, endOfDay } from "date-fns";
|
||||
import type { DateRange as ProfitDateRange } from "@/lib/services/profit-analytics-service";
|
||||
|
||||
// Lazy load chart components
|
||||
// Lazy load chart components - already handled individually below
|
||||
|
||||
const RevenueChart = dynamic(() => import("./RevenueChart"), {
|
||||
loading: () => <ChartSkeleton />,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, memo, useMemo, useCallback } from "react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
||||
@@ -5,8 +5,8 @@ const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'tr
|
||||
/** @type {import('next').NextConfig} */
|
||||
const baseConfig = {
|
||||
output: 'standalone',
|
||||
reactStrictMode: false,
|
||||
compress: process.env.NODE_ENV === 'production',
|
||||
// Enable compression for better performance
|
||||
compress: true,
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
@@ -58,10 +58,10 @@ const baseConfig = {
|
||||
transform: 'date-fns/{{member}}',
|
||||
},
|
||||
},
|
||||
// Webpack config (fallback if using --webpack flag)
|
||||
// Enhanced webpack config for better bundle splitting
|
||||
webpack: (config, { isServer, dev }) => {
|
||||
if (!isServer && !dev) {
|
||||
// Only apply aggressive code splitting in production
|
||||
// Enhanced code splitting for better performance
|
||||
config.optimization = {
|
||||
...config.optimization,
|
||||
moduleIds: 'deterministic',
|
||||
@@ -70,41 +70,62 @@ const baseConfig = {
|
||||
cacheGroups: {
|
||||
default: false,
|
||||
vendors: false,
|
||||
recharts: {
|
||||
test: /[\\/]node_modules[\\/]recharts[\\/]/,
|
||||
name: 'recharts',
|
||||
// Separate React and related libraries
|
||||
react: {
|
||||
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
|
||||
name: 'react',
|
||||
chunks: 'all',
|
||||
priority: 30,
|
||||
priority: 50,
|
||||
enforce: true,
|
||||
},
|
||||
// Separate UI library components
|
||||
radix: {
|
||||
test: /[\\/]node_modules[\\/]@radix-ui[\\/]/,
|
||||
name: 'radix-ui',
|
||||
chunks: 'all',
|
||||
priority: 40,
|
||||
},
|
||||
// Separate charting libraries
|
||||
charts: {
|
||||
test: /[\\/]node_modules[\\/](recharts|d3-.*)[\\/]/,
|
||||
name: 'charts',
|
||||
chunks: 'all',
|
||||
priority: 35,
|
||||
},
|
||||
// Separate icon libraries
|
||||
icons: {
|
||||
test: /[\\/]node_modules[\\/]lucide-react[\\/]/,
|
||||
name: 'icons',
|
||||
chunks: 'all',
|
||||
priority: 30,
|
||||
},
|
||||
// Separate date utilities
|
||||
dateUtils: {
|
||||
test: /[\\/]node_modules[\\/]date-fns[\\/]/,
|
||||
name: 'date-utils',
|
||||
chunks: 'all',
|
||||
priority: 25,
|
||||
},
|
||||
reactMarkdown: {
|
||||
test: /[\\/]node_modules[\\/]react-markdown[\\/]/,
|
||||
name: 'react-markdown',
|
||||
// Separate form libraries
|
||||
forms: {
|
||||
test: /[\\/]node_modules[\\/](react-hook-form|@hookform)[\\/]/,
|
||||
name: 'forms',
|
||||
chunks: 'all',
|
||||
priority: 20,
|
||||
},
|
||||
dateFns: {
|
||||
test: /[\\/]node_modules[\\/]date-fns[\\/]/,
|
||||
name: 'date-fns',
|
||||
// Group remaining vendor libraries
|
||||
vendor: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: 'vendor',
|
||||
chunks: 'all',
|
||||
priority: 15,
|
||||
},
|
||||
framework: {
|
||||
name: 'framework',
|
||||
chunks: 'all',
|
||||
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
|
||||
priority: 40,
|
||||
priority: 10,
|
||||
enforce: true,
|
||||
},
|
||||
commons: {
|
||||
name: 'commons',
|
||||
minChunks: 2,
|
||||
priority: 10,
|
||||
// Common application code
|
||||
common: {
|
||||
name: 'common',
|
||||
minChunks: 3,
|
||||
priority: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -112,10 +133,6 @@ const baseConfig = {
|
||||
}
|
||||
return config;
|
||||
},
|
||||
onDemandEntries: {
|
||||
maxInactiveAge: 15 * 1000,
|
||||
pagesBufferLength: 2,
|
||||
},
|
||||
productionBrowserSourceMaps: false,
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"@radix-ui/react-toggle": "^1.1.1",
|
||||
"@radix-ui/react-toggle-group": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"@tanstack/react-virtual": "^3.13.18",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.8.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -64,6 +65,8 @@
|
||||
"react-hook-form": "^7.54.1",
|
||||
"react-markdown": "^10.0.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-window": "^2.2.4",
|
||||
"react-window-infinite-loader": "^2.0.0",
|
||||
"recharts": "^2.15.0",
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
@@ -72,7 +75,6 @@
|
||||
"zod": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@distube/ytdl-core": "^4.16.12",
|
||||
"@next/bundle-analyzer": "^16.1.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@types/lodash": "^4.17.16",
|
||||
|
||||
149
pnpm-lock.yaml
generated
149
pnpm-lock.yaml
generated
@@ -86,6 +86,9 @@ importers:
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.1.6
|
||||
version: 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.13.18
|
||||
version: 3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.23(postcss@8.5.6)
|
||||
@@ -149,6 +152,12 @@ importers:
|
||||
react-resizable-panels:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
react-window:
|
||||
specifier: ^2.2.4
|
||||
version: 2.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
react-window-infinite-loader:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
recharts:
|
||||
specifier: ^2.15.0
|
||||
version: 2.15.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@@ -168,9 +177,6 @@ importers:
|
||||
specifier: ^3.25.0
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@distube/ytdl-core':
|
||||
specifier: ^4.16.12
|
||||
version: 4.16.12
|
||||
'@next/bundle-analyzer':
|
||||
specifier: ^16.1.1
|
||||
version: 16.1.1
|
||||
@@ -289,10 +295,6 @@ packages:
|
||||
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
'@distube/ytdl-core@4.16.12':
|
||||
resolution: {integrity: sha512-/NR8Jur1Q4E2oD+DJta7uwWu7SkqdEkhwERt7f4iune70zg7ZlLLTOHs1+jgg3uD2jQjpdk7RGC16FqstG4RsA==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
'@emnapi/core@1.8.1':
|
||||
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
|
||||
|
||||
@@ -1258,6 +1260,15 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
|
||||
|
||||
'@tanstack/react-virtual@3.13.18':
|
||||
resolution: {integrity: sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A==}
|
||||
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
|
||||
|
||||
'@tanstack/virtual-core@3.13.18':
|
||||
resolution: {integrity: sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg==}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
|
||||
|
||||
@@ -1506,10 +1517,6 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
ajv@6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
|
||||
@@ -2252,20 +2259,6 @@ packages:
|
||||
html-url-attributes@3.0.1:
|
||||
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
||||
|
||||
http-cookie-agent@7.0.3:
|
||||
resolution: {integrity: sha512-EeZo7CGhfqPW6R006rJa4QtZZUpBygDa2HZH3DJqsTzTjyRE6foDBVQIv/pjVsxHC8z2GIdbB1Hvn9SRorP3WQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
peerDependencies:
|
||||
tough-cookie: ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||
undici: ^7.0.0
|
||||
peerDependenciesMeta:
|
||||
undici:
|
||||
optional: true
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -2524,10 +2517,6 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
|
||||
|
||||
m3u8stream@0.8.6:
|
||||
resolution: {integrity: sha512-LZj8kIVf9KCphiHmH7sbFQTVe4tOemb202fWwvJwR9W5ENW/1hxJN6ksAWGhQgSBSa3jyWhnjKU1Fw1GaOdbyA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
math-intrinsics@1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2635,10 +2624,6 @@ packages:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
miniget@4.2.3:
|
||||
resolution: {integrity: sha512-SjbDPDICJ1zT+ZvQwK0hUcRY4wxlhhNpHL9nJOB2MEAXRGagTljsO8MEDzQMTFf0Q8g4QNi8P9lEm/g7e+qgzA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
|
||||
@@ -2973,6 +2958,18 @@ packages:
|
||||
react: '>=16.6.0'
|
||||
react-dom: '>=16.6.0'
|
||||
|
||||
react-window-infinite-loader@2.0.0:
|
||||
resolution: {integrity: sha512-dioOyvShGheEqqFHcPNKCixCOc2evwb2VEt9sitfJfTZ1hir8m6b8W0CNBvcUj+8Y8IeWu4yb88DI7k88aYTQQ==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
|
||||
react-window@2.2.4:
|
||||
resolution: {integrity: sha512-FiZsQHvt2qbnTz6cN+/FXvX62v2xukQ+AajUivkm/Ivdp9rnU3bp0B1eDcCNpQXNaDBdqkEVGNYHlvIUGU9yBw==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
|
||||
react@19.2.3:
|
||||
resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3046,10 +3043,6 @@ packages:
|
||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
sax@1.4.4:
|
||||
resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==}
|
||||
engines: {node: '>=11.0.0'}
|
||||
|
||||
scheduler@0.27.0:
|
||||
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
|
||||
|
||||
@@ -3219,13 +3212,6 @@ packages:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tldts-core@6.1.86:
|
||||
resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
|
||||
|
||||
tldts@6.1.86:
|
||||
resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
|
||||
hasBin: true
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
@@ -3234,10 +3220,6 @@ packages:
|
||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tough-cookie@5.1.2:
|
||||
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
trim-lines@3.0.1:
|
||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||
|
||||
@@ -3298,10 +3280,6 @@ packages:
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
undici@7.18.2:
|
||||
resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unified@11.0.5:
|
||||
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
|
||||
|
||||
@@ -3544,18 +3522,6 @@ snapshots:
|
||||
|
||||
'@discoveryjs/json-ext@0.5.7': {}
|
||||
|
||||
'@distube/ytdl-core@4.16.12':
|
||||
dependencies:
|
||||
http-cookie-agent: 7.0.3(tough-cookie@5.1.2)(undici@7.18.2)
|
||||
https-proxy-agent: 7.0.6
|
||||
m3u8stream: 0.8.6
|
||||
miniget: 4.2.3
|
||||
sax: 1.4.4
|
||||
tough-cookie: 5.1.2
|
||||
undici: 7.18.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@emnapi/core@1.8.1':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.1.0
|
||||
@@ -4512,6 +4478,14 @@ snapshots:
|
||||
postcss-selector-parser: 6.0.10
|
||||
tailwindcss: 3.4.19
|
||||
|
||||
'@tanstack/react-virtual@3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.13.18
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
'@tanstack/virtual-core@3.13.18': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@@ -4748,8 +4722,6 @@ snapshots:
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
ajv@6.12.6:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
@@ -5670,20 +5642,6 @@ snapshots:
|
||||
|
||||
html-url-attributes@3.0.1: {}
|
||||
|
||||
http-cookie-agent@7.0.3(tough-cookie@5.1.2)(undici@7.18.2):
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
tough-cookie: 5.1.2
|
||||
optionalDependencies:
|
||||
undici: 7.18.2
|
||||
|
||||
https-proxy-agent@7.0.6:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
ignore@7.0.5: {}
|
||||
@@ -5926,11 +5884,6 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.3
|
||||
|
||||
m3u8stream@0.8.6:
|
||||
dependencies:
|
||||
miniget: 4.2.3
|
||||
sax: 1.4.4
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
mdast-util-from-markdown@2.0.2:
|
||||
@@ -6168,8 +6121,6 @@ snapshots:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
miniget@4.2.3: {}
|
||||
|
||||
minimatch@3.1.2:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.12
|
||||
@@ -6495,6 +6446,16 @@ snapshots:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
react-window-infinite-loader@2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
react-window@2.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
|
||||
react@19.2.3: {}
|
||||
|
||||
read-cache@1.0.0:
|
||||
@@ -6604,8 +6565,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
is-regex: 1.2.1
|
||||
|
||||
sax@1.4.4: {}
|
||||
|
||||
scheduler@0.27.0: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
@@ -6861,22 +6820,12 @@ snapshots:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
tldts-core@6.1.86: {}
|
||||
|
||||
tldts@6.1.86:
|
||||
dependencies:
|
||||
tldts-core: 6.1.86
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tough-cookie@5.1.2:
|
||||
dependencies:
|
||||
tldts: 6.1.86
|
||||
|
||||
trim-lines@3.0.1: {}
|
||||
|
||||
trough@2.2.0: {}
|
||||
@@ -6955,8 +6904,6 @@ snapshots:
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
undici@7.18.2: {}
|
||||
|
||||
unified@11.0.5:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
@@ -1,114 +1,122 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/**/*.{ts,tsx}",
|
||||
"./lib/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./hooks/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./utils/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
shimmer: {
|
||||
'0%': { transform: 'translateX(-100%)' },
|
||||
'100%': { transform: 'translateX(100%)' }
|
||||
},
|
||||
twinkle: {
|
||||
'0%, 100%': { opacity: '1', transform: 'scale(1)' },
|
||||
'50%': { opacity: '0.3', transform: 'scale(0.8)' }
|
||||
},
|
||||
snowflake: {
|
||||
'0%': { transform: 'translateY(-100vh) rotate(0deg)', opacity: '1' },
|
||||
'100%': { transform: 'translateY(100vh) rotate(360deg)', opacity: '0' }
|
||||
},
|
||||
sparkle: {
|
||||
'0%, 100%': { opacity: '0', transform: 'scale(0) rotate(0deg)' },
|
||||
'50%': { opacity: '1', transform: 'scale(1) rotate(180deg)' }
|
||||
},
|
||||
glow: {
|
||||
'0%, 100%': {
|
||||
boxShadow: '0 0 5px hsl(0 84% 50%), 0 0 10px hsl(0 84% 50%), 0 0 15px hsl(0 84% 50%)'
|
||||
},
|
||||
'50%': {
|
||||
boxShadow: '0 0 10px hsl(142 76% 36%), 0 0 20px hsl(142 76% 36%), 0 0 30px hsl(142 76% 36%)'
|
||||
}
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"shimmer": "shimmer 2s infinite",
|
||||
"twinkle": "twinkle 2s ease-in-out infinite",
|
||||
"snowflake": "snowflake 10s linear infinite",
|
||||
"sparkle": "sparkle 1.5s ease-in-out infinite",
|
||||
"glow": "glow 2s ease-in-out infinite"
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
shimmer: {
|
||||
'0%': { transform: 'translateX(-100%)' },
|
||||
'100%': { transform: 'translateX(100%)' }
|
||||
},
|
||||
twinkle: {
|
||||
'0%, 100%': { opacity: '1', transform: 'scale(1)' },
|
||||
'50%': { opacity: '0.3', transform: 'scale(0.8)' }
|
||||
},
|
||||
snowflake: {
|
||||
'0%': { transform: 'translateY(-100vh) rotate(0deg)', opacity: '1' },
|
||||
'100%': { transform: 'translateY(100vh) rotate(360deg)', opacity: '0' }
|
||||
},
|
||||
sparkle: {
|
||||
'0%, 100%': { opacity: '0', transform: 'scale(0) rotate(0deg)' },
|
||||
'50%': { opacity: '1', transform: 'scale(1) rotate(180deg)' }
|
||||
},
|
||||
glow: {
|
||||
'0%, 100%': {
|
||||
boxShadow: '0 0 5px hsl(0 84% 50%), 0 0 10px hsl(0 84% 50%), 0 0 15px hsl(0 84% 50%)'
|
||||
},
|
||||
'50%': {
|
||||
boxShadow: '0 0 10px hsl(142 76% 36%), 0 0 20px hsl(142 76% 36%), 0 0 30px hsl(142 76% 36%)'
|
||||
}
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"shimmer": "shimmer 2s infinite",
|
||||
"twinkle": "twinkle 2s ease-in-out infinite",
|
||||
"snowflake": "snowflake 10s linear infinite",
|
||||
"sparkle": "sparkle 1.5s ease-in-out infinite",
|
||||
"glow": "glow 2s ease-in-out infinite"
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
|
||||
// Optimize for production
|
||||
future: {
|
||||
hoverOnlyWhenSupported: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
Reference in New Issue
Block a user