From 1f43e5b5e57bd7fed9dd320b62aa26c1f73cc096 Mon Sep 17 00:00:00 2001
From: Abul
Date: Thu, 20 Nov 2025 19:40:23 +0530
Subject: [PATCH 01/14] Add file viewing and improve download handling
---
app/api/files/[id]/download/route.ts | 6 ++-
app/api/files/[id]/view/route.ts | 50 ++++++++++++++++++++++++
components/modals/ViewFileModal.tsx | 28 +++++++++++---
lib/services/azure-storage.ts | 58 ++++++++++++++++++++++++----
4 files changed, 129 insertions(+), 13 deletions(-)
create mode 100644 app/api/files/[id]/view/route.ts
diff --git a/app/api/files/[id]/download/route.ts b/app/api/files/[id]/download/route.ts
index 9ec1913..fb460da 100644
--- a/app/api/files/[id]/download/route.ts
+++ b/app/api/files/[id]/download/route.ts
@@ -40,7 +40,11 @@ export async function GET(
return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404);
}
- const downloadUrl = await generateDownloadUrl(user!.id, file.fileName);
+ const downloadUrl = await generateDownloadUrl(
+ user!.id,
+ file.fileName,
+ file.originalFileName
+ );
return createSuccessResponse({ downloadUrl });
} catch (error) {
diff --git a/app/api/files/[id]/view/route.ts b/app/api/files/[id]/view/route.ts
new file mode 100644
index 0000000..2357589
--- /dev/null
+++ b/app/api/files/[id]/view/route.ts
@@ -0,0 +1,50 @@
+import { NextRequest } from "next/server";
+import { connectToDatabase } from "@/lib/services/mongodb";
+import { generateViewUrl } from "@/lib/services/azure-storage";
+import {
+ getAuthenticatedUser,
+ validateRequest,
+ createErrorResponse,
+ createSuccessResponse,
+} from "@/lib/api/api-helpers";
+import { fileIdSchema } from "@/lib/utils/validations";
+import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants";
+import { ObjectId } from "mongodb";
+import type { FileDocument } from "@/types";
+
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ id: string }> }
+) {
+ try {
+ const { error, user } = await getAuthenticatedUser(request);
+ if (error) {
+ return error;
+ }
+
+ const { id } = await params;
+
+ const idValidation = await validateRequest(fileIdSchema, { id });
+ if (!idValidation.success) {
+ return createErrorResponse(idValidation.error, 400);
+ }
+
+ const { db } = await connectToDatabase();
+
+ const file = await db.collection(COLLECTIONS.FILES).findOne({
+ _id: new ObjectId(id),
+ userId: user!.id,
+ });
+
+ if (!file) {
+ return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404);
+ }
+
+ const viewUrl = await generateViewUrl(user!.id, file.fileName);
+
+ return createSuccessResponse({ viewUrl });
+ } catch (error) {
+ console.error("View error:", error);
+ return createErrorResponse("Failed to generate view URL", 500);
+ }
+}
diff --git a/components/modals/ViewFileModal.tsx b/components/modals/ViewFileModal.tsx
index 8a4b3ff..ffab5de 100644
--- a/components/modals/ViewFileModal.tsx
+++ b/components/modals/ViewFileModal.tsx
@@ -32,10 +32,10 @@ export function ViewFileModal({ file, isOpen, onClose }: ViewFileModalProps) {
setLoading(true);
try {
- const response = await fetch(`/api/files/${file._id}/download`);
+ const response = await fetch(`/api/files/${file._id}/view`);
if (response.ok) {
const data = await response.json();
- setFileUrl(data.downloadUrl);
+ setFileUrl(data.viewUrl);
} else {
toast.error("Failed to load file");
}
@@ -55,9 +55,27 @@ export function ViewFileModal({ file, isOpen, onClose }: ViewFileModalProps) {
}
}, [file, isOpen, fetchFileUrl]);
- const handleDownload = () => {
- if (fileUrl) {
- window.open(fileUrl, "_blank");
+ const handleDownload = async () => {
+ if (!file) {
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/files/${file._id}/download`);
+ if (response.ok) {
+ const data = await response.json();
+ const link = document.createElement("a");
+ link.href = data.downloadUrl;
+ link.download = file.originalFileName;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ } else {
+ toast.error("Failed to download file");
+ }
+ } catch (error) {
+ console.error("Download error:", error);
+ toast.error("Failed to download file");
}
};
diff --git a/lib/services/azure-storage.ts b/lib/services/azure-storage.ts
index 255e964..606b91d 100644
--- a/lib/services/azure-storage.ts
+++ b/lib/services/azure-storage.ts
@@ -21,10 +21,7 @@ const containerName = process.env.AZURE_STORAGE_CONTAINER_NAME || "user-files";
export async function getContainerClient(): Promise {
const containerClient = blobServiceClient.getContainerClient(containerName);
- // Ensure container exists
- await containerClient.createIfNotExists({
- access: "blob",
- });
+ await containerClient.createIfNotExists();
return containerClient;
}
@@ -59,15 +56,62 @@ export async function deleteFileFromBlob(
await blockBlobClient.deleteIfExists();
}
+export async function generateViewUrl(
+ userId: string,
+ fileName: string,
+ expiryHours: number = 2
+): Promise {
+ const containerClient = await getContainerClient();
+ const blobName = `${userId}/${fileName}`;
+ const blockBlobClient = containerClient.getBlockBlobClient(blobName);
+
+ const sharedKeyCredential = getSharedKeyCredential();
+
+ const startsOn = new Date();
+ const expiresOn = new Date(startsOn.getTime() + expiryHours * 60 * 60 * 1000);
+
+ const sasToken = generateBlobSASQueryParameters(
+ {
+ containerName: containerClient.containerName,
+ blobName,
+ permissions: BlobSASPermissions.parse("r"),
+ startsOn,
+ expiresOn,
+ },
+ sharedKeyCredential
+ ).toString();
+
+ return `${blockBlobClient.url}?${sasToken}`;
+}
+
export async function generateDownloadUrl(
userId: string,
- fileName: string
+ fileName: string,
+ originalFileName: string,
+ expiryMinutes: number = 10
): Promise {
const containerClient = await getContainerClient();
const blobName = `${userId}/${fileName}`;
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
- return blockBlobClient.url;
+ const sharedKeyCredential = getSharedKeyCredential();
+
+ const startsOn = new Date();
+ const expiresOn = new Date(startsOn.getTime() + expiryMinutes * 60 * 1000);
+
+ const sasToken = generateBlobSASQueryParameters(
+ {
+ containerName: containerClient.containerName,
+ blobName,
+ permissions: BlobSASPermissions.parse("r"),
+ startsOn,
+ expiresOn,
+ contentDisposition: `attachment; filename="${originalFileName}"`,
+ },
+ sharedKeyCredential
+ ).toString();
+
+ return `${blockBlobClient.url}?${sasToken}`;
}
function getSharedKeyCredential(): StorageSharedKeyCredential {
@@ -102,7 +146,7 @@ export async function generatePresignedUploadUrl(
const sasToken = generateBlobSASQueryParameters(
{
- containerName,
+ containerName: containerClient.containerName,
blobName,
permissions: BlobSASPermissions.parse("w"),
startsOn,
From 92efbfb4cac7211aa9d1308fd3fb12adbf5dbe4e Mon Sep 17 00:00:00 2001
From: Abul
Date: Fri, 21 Nov 2025 22:00:47 +0530
Subject: [PATCH 02/14] Implement activity logging and add activity dashboard
---
app/api/activity/route.ts | 32 ++++++
app/api/files/[id]/download/route.ts | 8 ++
app/api/files/[id]/restore/route.ts | 8 ++
app/api/files/[id]/route.ts | 51 +++++++--
app/api/files/[id]/view/route.ts | 8 ++
app/api/files/recent/route.ts | 73 ++++++++++++
app/api/upload/complete/route.ts | 8 ++
app/dashboard/activity/page.tsx | 90 +++++++++++++++
app/dashboard/recent/page.tsx | 41 +++++++
components/activity/ActivityList.tsx | 153 +++++++++++++++++++++++++
components/layout/SidebarComponent.tsx | 14 +++
lib/config/constants.ts | 1 +
lib/services/activity-logger.ts | 37 ++++++
lib/utils/utils.ts | 33 ++++++
types/index.ts | 21 ++++
15 files changed, 566 insertions(+), 12 deletions(-)
create mode 100644 app/api/activity/route.ts
create mode 100644 app/api/files/recent/route.ts
create mode 100644 app/dashboard/activity/page.tsx
create mode 100644 app/dashboard/recent/page.tsx
create mode 100644 components/activity/ActivityList.tsx
create mode 100644 lib/services/activity-logger.ts
diff --git a/app/api/activity/route.ts b/app/api/activity/route.ts
new file mode 100644
index 0000000..61502c1
--- /dev/null
+++ b/app/api/activity/route.ts
@@ -0,0 +1,32 @@
+import { NextRequest } from "next/server";
+import { connectToDatabase } from "@/lib/services/mongodb";
+import {
+ getAuthenticatedUser,
+ createErrorResponse,
+ createSuccessResponse,
+} from "@/lib/api/api-helpers";
+import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants";
+import type { ActivityLog } from "@/types";
+
+export async function GET(request: NextRequest) {
+ try {
+ const { error, user } = await getAuthenticatedUser(request);
+ if (error) {
+ return error;
+ }
+
+ const { db } = await connectToDatabase();
+
+ const activities = await db
+ .collection(COLLECTIONS.ACTIVITIES)
+ .find({ userId: user!.id })
+ .sort({ timestamp: -1 })
+ .limit(100)
+ .toArray();
+
+ return createSuccessResponse({ activities });
+ } catch (error) {
+ console.error("Activity fetch error:", error);
+ return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
+ }
+}
diff --git a/app/api/files/[id]/download/route.ts b/app/api/files/[id]/download/route.ts
index fb460da..90ca38a 100644
--- a/app/api/files/[id]/download/route.ts
+++ b/app/api/files/[id]/download/route.ts
@@ -1,6 +1,7 @@
import { NextRequest } from "next/server";
import { connectToDatabase } from "@/lib/services/mongodb";
import { generateDownloadUrl } from "@/lib/services/azure-storage";
+import { logActivity } from "@/lib/services/activity-logger";
import {
getAuthenticatedUser,
validateRequest,
@@ -46,6 +47,13 @@ export async function GET(
file.originalFileName
);
+ await logActivity({
+ userId: user!.id,
+ fileId: file._id!,
+ action: "download",
+ fileName: file.originalFileName,
+ });
+
return createSuccessResponse({ downloadUrl });
} catch (error) {
console.error("Download error:", error);
diff --git a/app/api/files/[id]/restore/route.ts b/app/api/files/[id]/restore/route.ts
index 2db08c6..b67f695 100644
--- a/app/api/files/[id]/restore/route.ts
+++ b/app/api/files/[id]/restore/route.ts
@@ -1,6 +1,7 @@
import { NextRequest } from "next/server";
import { connectToDatabase } from "@/lib/services/mongodb";
import { validateCSRFToken, createCSRFError } from "@/lib/auth/csrf-middleware";
+import { logActivity } from "@/lib/services/activity-logger";
import {
getAuthenticatedUser,
validateRequest,
@@ -52,6 +53,13 @@ export async function PATCH(
}
);
+ await logActivity({
+ userId: user!.id,
+ fileId: file._id!,
+ action: "restore",
+ fileName: file.originalFileName,
+ });
+
return createSuccessResponse({
success: true,
message: "File restored successfully",
diff --git a/app/api/files/[id]/route.ts b/app/api/files/[id]/route.ts
index d890f8f..d77763b 100644
--- a/app/api/files/[id]/route.ts
+++ b/app/api/files/[id]/route.ts
@@ -1,6 +1,7 @@
import { NextRequest } from "next/server";
import { connectToDatabase } from "@/lib/services/mongodb";
import { validateCSRFToken, createCSRFError } from "@/lib/auth/csrf-middleware";
+import { logActivity } from "@/lib/services/activity-logger";
import {
getAuthenticatedUser,
validateRequest,
@@ -46,22 +47,41 @@ export async function PATCH(
const { db } = await connectToDatabase();
- const result = await db
- .collection(COLLECTIONS.FILES)
- .updateOne(
- {
- _id: new ObjectId(id),
- userId: user!.id,
- },
- {
- $set: validation.data,
- }
- );
+ const file = await db.collection(COLLECTIONS.FILES).findOne({
+ _id: new ObjectId(id),
+ userId: user!.id,
+ });
- if (result.matchedCount === 0) {
+ if (!file) {
return createErrorResponse(ERROR_MESSAGES.FILE_NOT_FOUND, 404);
}
+ await db.collection(COLLECTIONS.FILES).updateOne(
+ {
+ _id: new ObjectId(id),
+ userId: user!.id,
+ },
+ {
+ $set: validation.data,
+ }
+ );
+
+ if (
+ validation.data.originalFileName &&
+ validation.data.originalFileName !== file.originalFileName
+ ) {
+ await logActivity({
+ userId: user!.id,
+ fileId: file._id!,
+ action: "rename",
+ fileName: validation.data.originalFileName,
+ metadata: {
+ oldName: file.originalFileName,
+ newName: validation.data.originalFileName,
+ },
+ });
+ }
+
return createSuccessResponse({ success: true, ...validation.data });
} catch (error) {
console.error("File update error:", error);
@@ -109,6 +129,13 @@ export async function DELETE(
}
);
+ await logActivity({
+ userId: user!.id,
+ fileId: file._id!,
+ action: "delete",
+ fileName: file.originalFileName,
+ });
+
return createSuccessResponse({
success: true,
message: "File moved to trash",
diff --git a/app/api/files/[id]/view/route.ts b/app/api/files/[id]/view/route.ts
index 2357589..22a2c92 100644
--- a/app/api/files/[id]/view/route.ts
+++ b/app/api/files/[id]/view/route.ts
@@ -1,6 +1,7 @@
import { NextRequest } from "next/server";
import { connectToDatabase } from "@/lib/services/mongodb";
import { generateViewUrl } from "@/lib/services/azure-storage";
+import { logActivity } from "@/lib/services/activity-logger";
import {
getAuthenticatedUser,
validateRequest,
@@ -42,6 +43,13 @@ export async function GET(
const viewUrl = await generateViewUrl(user!.id, file.fileName);
+ await logActivity({
+ userId: user!.id,
+ fileId: file._id!,
+ action: "view",
+ fileName: file.originalFileName,
+ });
+
return createSuccessResponse({ viewUrl });
} catch (error) {
console.error("View error:", error);
diff --git a/app/api/files/recent/route.ts b/app/api/files/recent/route.ts
new file mode 100644
index 0000000..262703c
--- /dev/null
+++ b/app/api/files/recent/route.ts
@@ -0,0 +1,73 @@
+import { NextRequest } from "next/server";
+import { connectToDatabase } from "@/lib/services/mongodb";
+import {
+ getAuthenticatedUser,
+ createErrorResponse,
+ createSuccessResponse,
+} from "@/lib/api/api-helpers";
+import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants";
+import type { FileDocument } from "@/types";
+
+export async function GET(request: NextRequest) {
+ try {
+ const { error, user } = await getAuthenticatedUser(request);
+ if (error) {
+ return error;
+ }
+
+ const { db } = await connectToDatabase();
+
+ const recentFiles = await db
+ .collection(COLLECTIONS.ACTIVITIES)
+ .aggregate([
+ {
+ $match: {
+ userId: user!.id,
+ action: { $in: ["upload", "view", "rename"] },
+ },
+ },
+ {
+ $sort: { timestamp: -1 },
+ },
+ {
+ $group: {
+ _id: "$fileId",
+ lastActivity: { $first: "$timestamp" },
+ },
+ },
+ {
+ $sort: { lastActivity: -1 },
+ },
+ {
+ $lookup: {
+ from: COLLECTIONS.FILES,
+ localField: "_id",
+ foreignField: "_id",
+ as: "file",
+ },
+ },
+ {
+ $unwind: "$file",
+ },
+ {
+ $match: {
+ "file.deletedAt": { $exists: false },
+ },
+ },
+ {
+ $replaceRoot: { newRoot: "$file" },
+ },
+ {
+ $limit: 20,
+ },
+ ])
+ .toArray();
+
+ const files = recentFiles as FileDocument[];
+
+ return createSuccessResponse({ files });
+ } catch (error) {
+ console.error("Recent files fetch error:", error);
+ return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
+ }
+}
diff --git a/app/api/upload/complete/route.ts b/app/api/upload/complete/route.ts
index 8ec9052..1b53baa 100644
--- a/app/api/upload/complete/route.ts
+++ b/app/api/upload/complete/route.ts
@@ -5,6 +5,7 @@ import {
verifyBlobExists,
getBlobProperties,
} from "@/lib/services/azure-storage";
+import { logActivity } from "@/lib/services/activity-logger";
import {
getAuthenticatedUser,
validateRequest,
@@ -109,6 +110,13 @@ export async function POST(request: NextRequest) {
}
);
+ await logActivity({
+ userId: user!.id,
+ fileId: result.insertedId,
+ action: "upload",
+ fileName: originalFileName,
+ });
+
return createSuccessResponse({
success: true,
file: { ...fileDocument, _id: result.insertedId },
diff --git a/app/dashboard/activity/page.tsx b/app/dashboard/activity/page.tsx
new file mode 100644
index 0000000..737a6f3
--- /dev/null
+++ b/app/dashboard/activity/page.tsx
@@ -0,0 +1,90 @@
+"use client";
+
+import { useState, useEffect, useCallback } from "react";
+import { useRequireAuth } from "@/hooks/auth/useRequireAuth";
+import { LoadingScreen } from "@/components/common/LoadingScreen";
+import { DashboardHeader } from "@/components/layout/DashboardHeader";
+import { Sidebar } from "@/components/layout/SidebarComponent";
+import { ActivityList } from "@/components/activity/ActivityList";
+import type { ActivityLog } from "@/types";
+
+export default function ActivityPage() {
+ const { session, status, isAuthenticated } = useRequireAuth();
+ const [activities, setActivities] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
+
+ const fetchActivities = useCallback(async () => {
+ try {
+ const response = await fetch("/api/activity");
+ if (response.ok) {
+ const data = await response.json();
+ setActivities(data.activities);
+ }
+ } catch (error) {
+ console.error("Failed to fetch activities:", error);
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ if (status === "loading" || !isAuthenticated) {
+ return;
+ }
+
+ fetchActivities();
+ }, [status, isAuthenticated, fetchActivities]);
+
+ if (status === "loading") {
+ return ;
+ }
+
+ if (!session) {
+ return null;
+ }
+
+ return (
+
+
setIsMobileSidebarOpen(true)} />
+
+
+ {/* Desktop Sidebar */}
+
+
+
+
+ {/* Mobile Sidebar Overlay */}
+ {isMobileSidebarOpen && (
+
setIsMobileSidebarOpen(false)}
+ >
+
e.stopPropagation()}
+ >
+
+
+
+ )}
+
+
+
+
+
+
+ Activity
+
+
+ Your recent file activities
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/dashboard/recent/page.tsx b/app/dashboard/recent/page.tsx
new file mode 100644
index 0000000..94ba4d2
--- /dev/null
+++ b/app/dashboard/recent/page.tsx
@@ -0,0 +1,41 @@
+"use client";
+
+import { DashboardLayout } from "@/components/layout/DashboardLayout";
+import { LoadingScreen } from "@/components/common/LoadingScreen";
+import { useDashboardPage } from "@/hooks/dashboard/useDashboardPage";
+
+export default function RecentPage() {
+ const {
+ status,
+ session,
+ files,
+ loading,
+ isUploadModalOpen,
+ setIsUploadModalOpen,
+ handleUploadSuccess,
+ handleFileAction,
+ handleNewClick,
+ } = useDashboardPage({ endpoint: "/api/files/recent" });
+
+ if (status === "loading") {
+ return ;
+ }
+
+ if (!session) {
+ return null;
+ }
+
+ return (
+ setIsUploadModalOpen(false)}
+ onUploadSuccess={handleUploadSuccess}
+ onFileAction={handleFileAction}
+ onNewClick={handleNewClick}
+ loadingMessage="Loading recent files..."
+ />
+ );
+}
diff --git a/components/activity/ActivityList.tsx b/components/activity/ActivityList.tsx
new file mode 100644
index 0000000..9ff6ae4
--- /dev/null
+++ b/components/activity/ActivityList.tsx
@@ -0,0 +1,153 @@
+import {
+ Upload,
+ Eye,
+ Download,
+ FileEdit,
+ Trash2,
+ RotateCcw,
+ Clock,
+} from "lucide-react";
+import { formatRelativeTime } from "@/lib/utils/utils";
+import type { ActivityLog, ActivityAction } from "@/types";
+
+interface ActivityListProps {
+ activities: ActivityLog[];
+ loading: boolean;
+}
+
+function getActivityIcon(action: ActivityAction) {
+ switch (action) {
+ case "upload":
+ return Upload;
+ case "view":
+ return Eye;
+ case "download":
+ return Download;
+ case "rename":
+ return FileEdit;
+ case "delete":
+ return Trash2;
+ case "restore":
+ return RotateCcw;
+ }
+}
+
+function getActivityColor(action: ActivityAction) {
+ switch (action) {
+ case "upload":
+ return "bg-green-500/10 text-green-600 dark:text-green-400";
+ case "view":
+ return "bg-blue-500/10 text-blue-600 dark:text-blue-400";
+ case "download":
+ return "bg-purple-500/10 text-purple-600 dark:text-purple-400";
+ case "rename":
+ return "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400";
+ case "delete":
+ return "bg-red-500/10 text-red-600 dark:text-red-400";
+ case "restore":
+ return "bg-cyan-500/10 text-cyan-600 dark:text-cyan-400";
+ }
+}
+
+function getActivityText(activity: ActivityLog) {
+ switch (activity.action) {
+ case "upload":
+ return (
+ <>
+ Uploaded {activity.fileName}
+ >
+ );
+ case "view":
+ return (
+ <>
+ Viewed {activity.fileName}
+ >
+ );
+ case "download":
+ return (
+ <>
+ Downloaded {activity.fileName}
+ >
+ );
+ case "rename":
+ return (
+ <>
+ Renamed{" "}
+ {activity.metadata?.oldName} to{" "}
+ {activity.metadata?.newName}
+ >
+ );
+ case "delete":
+ return (
+ <>
+ Deleted {activity.fileName}
+ >
+ );
+ case "restore":
+ return (
+ <>
+ Restored {activity.fileName}
+ >
+ );
+ }
+}
+
+export function ActivityList({ activities, loading }: ActivityListProps) {
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (activities.length === 0) {
+ return (
+
+
+
+
+ No activity yet
+
+
+ Your file activities will appear here
+
+
+
+ );
+ }
+
+ return (
+
+
+ {activities.map(activity => {
+ const Icon = getActivityIcon(activity.action);
+ const colorClass = getActivityColor(activity.action);
+
+ return (
+
+
+
+
+
+
+ {getActivityText(activity)}
+
+
+ {formatRelativeTime(activity.timestamp)}
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/components/layout/SidebarComponent.tsx b/components/layout/SidebarComponent.tsx
index fd656b6..82818a9 100644
--- a/components/layout/SidebarComponent.tsx
+++ b/components/layout/SidebarComponent.tsx
@@ -21,6 +21,7 @@ import {
Plus,
Settings,
HelpCircle,
+ Activity,
} from "lucide-react";
import { useEffect } from "react";
import { useStorage } from "@/components/providers/StorageContext";
@@ -43,6 +44,9 @@ export function Sidebar({ className, onNewClick }: SidebarProps) {
if (pathname === "/dashboard/recent") {
return "recent";
}
+ if (pathname === "/dashboard/activity") {
+ return "activity";
+ }
if (pathname === "/dashboard/trash") {
return "trash";
}
@@ -69,6 +73,10 @@ export function Sidebar({ className, onNewClick }: SidebarProps) {
router.push("/dashboard/starred");
break;
case "recent":
+ router.push("/dashboard/recent");
+ break;
+ case "activity":
+ router.push("/dashboard/activity");
break;
case "trash":
router.push("/dashboard/trash");
@@ -95,6 +103,12 @@ export function Sidebar({ className, onNewClick }: SidebarProps) {
icon: Star,
count: null,
},
+ {
+ id: "activity",
+ label: "Activity",
+ icon: Activity,
+ count: null,
+ },
{
id: "trash",
label: "Trash",
diff --git a/lib/config/constants.ts b/lib/config/constants.ts
index 3ed8f13..dbe4531 100644
--- a/lib/config/constants.ts
+++ b/lib/config/constants.ts
@@ -1,6 +1,7 @@
export const COLLECTIONS = {
USERS: "users",
FILES: "files",
+ ACTIVITIES: "activities",
} as const;
export const ERROR_MESSAGES = {
diff --git a/lib/services/activity-logger.ts b/lib/services/activity-logger.ts
new file mode 100644
index 0000000..891ad19
--- /dev/null
+++ b/lib/services/activity-logger.ts
@@ -0,0 +1,37 @@
+import { ObjectId } from "mongodb";
+import { connectToDatabase } from "./mongodb";
+import { ActivityAction, ActivityLog } from "@/types";
+import { COLLECTIONS } from "@/lib/config/constants";
+
+interface LogActivityParams {
+ userId: string;
+ fileId: string | ObjectId;
+ action: ActivityAction;
+ fileName: string;
+ metadata?: {
+ oldName?: string;
+ newName?: string;
+ };
+}
+
+export async function logActivity(params: LogActivityParams): Promise {
+ try {
+ const { db } = await connectToDatabase();
+
+ const activityLog: ActivityLog = {
+ userId: params.userId,
+ fileId:
+ typeof params.fileId === "string"
+ ? new ObjectId(params.fileId)
+ : params.fileId,
+ action: params.action,
+ fileName: params.fileName,
+ metadata: params.metadata,
+ timestamp: new Date(),
+ };
+
+ await db.collection(COLLECTIONS.ACTIVITIES).insertOne(activityLog);
+ } catch (error) {
+ console.error("Failed to log activity:", error);
+ }
+}
diff --git a/lib/utils/utils.ts b/lib/utils/utils.ts
index d997cfb..ed73c81 100644
--- a/lib/utils/utils.ts
+++ b/lib/utils/utils.ts
@@ -39,3 +39,36 @@ export function formatTimeRemaining(seconds: number): string {
const remainingSeconds = Math.round(seconds % 60);
return `${minutes}m ${remainingSeconds}s`;
}
+
+export function formatRelativeTime(date: Date | string): string {
+ const now = new Date();
+ const past = new Date(date);
+ const seconds = Math.floor((now.getTime() - past.getTime()) / 1000);
+
+ if (seconds < 60) {
+ return "just now";
+ }
+
+ const minutes = Math.floor(seconds / 60);
+ if (minutes < 60) {
+ return `${minutes} ${minutes === 1 ? "minute" : "minutes"} ago`;
+ }
+
+ const hours = Math.floor(minutes / 60);
+ if (hours < 24) {
+ return `${hours} ${hours === 1 ? "hour" : "hours"} ago`;
+ }
+
+ const days = Math.floor(hours / 24);
+ if (days < 30) {
+ return `${days} ${days === 1 ? "day" : "days"} ago`;
+ }
+
+ const months = Math.floor(days / 30);
+ if (months < 12) {
+ return `${months} ${months === 1 ? "month" : "months"} ago`;
+ }
+
+ const years = Math.floor(months / 12);
+ return `${years} ${years === 1 ? "year" : "years"} ago`;
+}
diff --git a/types/index.ts b/types/index.ts
index e584be3..54fbbc6 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -26,6 +26,27 @@ export interface FileDocument {
deletedAt?: Date; // When the file was moved to trash (soft delete)
}
+export type ActivityAction =
+ | "upload"
+ | "view"
+ | "download"
+ | "rename"
+ | "delete"
+ | "restore";
+
+export interface ActivityLog {
+ _id?: ObjectId;
+ userId: string;
+ fileId: ObjectId;
+ action: ActivityAction;
+ fileName: string;
+ metadata?: {
+ oldName?: string;
+ newName?: string;
+ };
+ timestamp: Date;
+}
+
// File upload response type
export interface FileUploadResponse {
success: boolean;
From 681c05d1a8a2195a0d0e2273eb241ae25beb1c3e Mon Sep 17 00:00:00 2001
From: Abul
Date: Fri, 21 Nov 2025 23:01:20 +0530
Subject: [PATCH 03/14] Implement pagination for activity and file retrieval
---
app/api/activity/route.ts | 35 +++++--
app/api/files/recent/route.ts | 104 +++++++++++---------
app/api/files/route.ts | 40 ++++++--
app/api/files/starred/route.ts | 42 +++++++--
app/dashboard/activity/page.tsx | 78 ++++++++++++---
app/dashboard/page.tsx | 10 +-
app/dashboard/recent/page.tsx | 10 +-
app/dashboard/starred/page.tsx | 10 +-
app/dashboard/trash/page.tsx | 10 +-
components/common/PaginationControls.tsx | 115 +++++++++++++++++++++++
components/layout/DashboardLayout.tsx | 41 ++++++--
hooks/dashboard/useDashboardPage.ts | 83 ++++++++++++----
12 files changed, 470 insertions(+), 108 deletions(-)
create mode 100644 components/common/PaginationControls.tsx
diff --git a/app/api/activity/route.ts b/app/api/activity/route.ts
index 61502c1..418564c 100644
--- a/app/api/activity/route.ts
+++ b/app/api/activity/route.ts
@@ -15,16 +15,37 @@ export async function GET(request: NextRequest) {
return error;
}
+ const { searchParams } = new URL(request.url);
+ const page = parseInt(searchParams.get("page") || "1", 10);
+ const limit = parseInt(searchParams.get("limit") || "7", 10);
+ const skip = (page - 1) * limit;
+
const { db } = await connectToDatabase();
- const activities = await db
- .collection(COLLECTIONS.ACTIVITIES)
- .find({ userId: user!.id })
- .sort({ timestamp: -1 })
- .limit(100)
- .toArray();
+ const query = { userId: user!.id };
+
+ const [activities, totalCount] = await Promise.all([
+ db
+ .collection(COLLECTIONS.ACTIVITIES)
+ .find(query)
+ .sort({ timestamp: -1 })
+ .skip(skip)
+ .limit(limit)
+ .toArray(),
+ db.collection(COLLECTIONS.ACTIVITIES).countDocuments(query),
+ ]);
+
+ const totalPages = Math.ceil(totalCount / limit);
- return createSuccessResponse({ activities });
+ return createSuccessResponse({
+ activities,
+ pagination: {
+ currentPage: page,
+ totalPages,
+ totalCount,
+ limit,
+ },
+ });
} catch (error) {
console.error("Activity fetch error:", error);
return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
diff --git a/app/api/files/recent/route.ts b/app/api/files/recent/route.ts
index 262703c..2151415 100644
--- a/app/api/files/recent/route.ts
+++ b/app/api/files/recent/route.ts
@@ -15,57 +15,77 @@ export async function GET(request: NextRequest) {
return error;
}
+ const { searchParams } = new URL(request.url);
+ const page = parseInt(searchParams.get("page") || "1", 10);
+ const limit = parseInt(searchParams.get("limit") || "12", 10);
+ const skip = (page - 1) * limit;
+
const { db } = await connectToDatabase();
- const recentFiles = await db
- .collection(COLLECTIONS.ACTIVITIES)
- .aggregate([
- {
- $match: {
- userId: user!.id,
- action: { $in: ["upload", "view", "rename"] },
- },
- },
- {
- $sort: { timestamp: -1 },
- },
- {
- $group: {
- _id: "$fileId",
- lastActivity: { $first: "$timestamp" },
- },
- },
- {
- $sort: { lastActivity: -1 },
+ const basePipeline = [
+ {
+ $match: {
+ userId: user!.id,
+ action: { $in: ["upload", "view", "rename"] },
},
- {
- $lookup: {
- from: COLLECTIONS.FILES,
- localField: "_id",
- foreignField: "_id",
- as: "file",
- },
+ },
+ {
+ $sort: { timestamp: -1 },
+ },
+ {
+ $group: {
+ _id: "$fileId",
+ lastActivity: { $first: "$timestamp" },
},
- {
- $unwind: "$file",
+ },
+ {
+ $sort: { lastActivity: -1 },
+ },
+ {
+ $lookup: {
+ from: COLLECTIONS.FILES,
+ localField: "_id",
+ foreignField: "_id",
+ as: "file",
},
- {
- $match: {
- "file.deletedAt": { $exists: false },
- },
+ },
+ {
+ $unwind: "$file",
+ },
+ {
+ $match: {
+ "file.deletedAt": { $exists: false },
},
- {
- $replaceRoot: { newRoot: "$file" },
- },
- {
- $limit: 20,
- },
- ])
- .toArray();
+ },
+ {
+ $replaceRoot: { newRoot: "$file" },
+ },
+ ];
+
+ const [recentFiles, countResult] = await Promise.all([
+ db
+ .collection(COLLECTIONS.ACTIVITIES)
+ .aggregate([...basePipeline, { $skip: skip }, { $limit: limit }])
+ .toArray(),
+ db
+ .collection(COLLECTIONS.ACTIVITIES)
+ .aggregate([...basePipeline, { $count: "total" }])
+ .toArray(),
+ ]);
const files = recentFiles as FileDocument[];
+ const totalCount = countResult[0]?.total || 0;
+ const totalPages = Math.ceil(totalCount / limit);
- return createSuccessResponse({ files });
+ return createSuccessResponse({
+ files,
+ pagination: {
+ currentPage: page,
+ totalPages,
+ totalCount,
+ limit,
+ },
+ });
} catch (error) {
console.error("Recent files fetch error:", error);
return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
diff --git a/app/api/files/route.ts b/app/api/files/route.ts
index 664d57c..41ee516 100644
--- a/app/api/files/route.ts
+++ b/app/api/files/route.ts
@@ -15,18 +15,40 @@ export async function GET(request: NextRequest) {
return error;
}
+ const { searchParams } = new URL(request.url);
+ const page = parseInt(searchParams.get("page") || "1", 10);
+ const limit = parseInt(searchParams.get("limit") || "18", 10);
+ const skip = (page - 1) * limit;
+
const { db } = await connectToDatabase();
- const files = await db
- .collection(COLLECTIONS.FILES)
- .find({
- userId: user!.id,
- deletedAt: { $exists: false },
- })
- .sort({ uploadedAt: -1 })
- .toArray();
+ const query = {
+ userId: user!.id,
+ deletedAt: { $exists: false },
+ };
+
+ const [files, totalCount] = await Promise.all([
+ db
+ .collection(COLLECTIONS.FILES)
+ .find(query)
+ .sort({ uploadedAt: -1 })
+ .skip(skip)
+ .limit(limit)
+ .toArray(),
+ db.collection(COLLECTIONS.FILES).countDocuments(query),
+ ]);
+
+ const totalPages = Math.ceil(totalCount / limit);
- return createSuccessResponse({ files });
+ return createSuccessResponse({
+ files,
+ pagination: {
+ currentPage: page,
+ totalPages,
+ totalCount,
+ limit,
+ },
+ });
} catch (error) {
console.error("Files fetch error:", error);
return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
diff --git a/app/api/files/starred/route.ts b/app/api/files/starred/route.ts
index fb2e430..5adf97c 100644
--- a/app/api/files/starred/route.ts
+++ b/app/api/files/starred/route.ts
@@ -15,19 +15,41 @@ export async function GET(request: NextRequest) {
return error;
}
+ const { searchParams } = new URL(request.url);
+ const page = parseInt(searchParams.get("page") || "1", 10);
+ const limit = parseInt(searchParams.get("limit") || "12", 10);
+ const skip = (page - 1) * limit;
+
const { db } = await connectToDatabase();
- const files = await db
- .collection(COLLECTIONS.FILES)
- .find({
- userId: user!.id,
- starred: true,
- deletedAt: { $exists: false },
- })
- .sort({ uploadedAt: -1 })
- .toArray();
+ const query = {
+ userId: user!.id,
+ starred: true,
+ deletedAt: { $exists: false },
+ };
+
+ const [files, totalCount] = await Promise.all([
+ db
+ .collection(COLLECTIONS.FILES)
+ .find(query)
+ .sort({ uploadedAt: -1 })
+ .skip(skip)
+ .limit(limit)
+ .toArray(),
+ db.collection(COLLECTIONS.FILES).countDocuments(query),
+ ]);
+
+ const totalPages = Math.ceil(totalCount / limit);
- return createSuccessResponse({ files });
+ return createSuccessResponse({
+ files,
+ pagination: {
+ currentPage: page,
+ totalPages,
+ totalCount,
+ limit,
+ },
+ });
} catch (error) {
console.error("Starred files fetch error:", error);
return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
diff --git a/app/dashboard/activity/page.tsx b/app/dashboard/activity/page.tsx
index 737a6f3..d8a90d2 100644
--- a/app/dashboard/activity/page.tsx
+++ b/app/dashboard/activity/page.tsx
@@ -6,36 +6,76 @@ import { LoadingScreen } from "@/components/common/LoadingScreen";
import { DashboardHeader } from "@/components/layout/DashboardHeader";
import { Sidebar } from "@/components/layout/SidebarComponent";
import { ActivityList } from "@/components/activity/ActivityList";
+import { Button } from "@/components/ui/button";
import type { ActivityLog } from "@/types";
+interface PaginationMetadata {
+ currentPage: number;
+ totalPages: number;
+ totalCount: number;
+ limit: number;
+}
+
export default function ActivityPage() {
const { session, status, isAuthenticated } = useRequireAuth();
const [activities, setActivities] = useState([]);
const [loading, setLoading] = useState(true);
+ const [loadingMore, setLoadingMore] = useState(false);
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pagination, setPagination] = useState({
+ currentPage: 1,
+ totalPages: 1,
+ totalCount: 0,
+ limit: 7,
+ });
- const fetchActivities = useCallback(async () => {
- try {
- const response = await fetch("/api/activity");
- if (response.ok) {
- const data = await response.json();
- setActivities(data.activities);
+ const fetchActivities = useCallback(
+ async (page: number = 1, append: boolean = false) => {
+ if (append) {
+ setLoadingMore(true);
+ } else {
+ setLoading(true);
}
- } catch (error) {
- console.error("Failed to fetch activities:", error);
- } finally {
- setLoading(false);
- }
- }, []);
+ try {
+ const response = await fetch(`/api/activity?page=${page}&limit=7`);
+ if (response.ok) {
+ const data = await response.json();
+ if (append) {
+ setActivities(prev => [...prev, ...data.activities]);
+ } else {
+ setActivities(data.activities);
+ }
+ if (data.pagination) {
+ setPagination(data.pagination);
+ }
+ }
+ } catch (error) {
+ console.error("Failed to fetch activities:", error);
+ } finally {
+ setLoading(false);
+ setLoadingMore(false);
+ }
+ },
+ []
+ );
useEffect(() => {
if (status === "loading" || !isAuthenticated) {
return;
}
- fetchActivities();
+ fetchActivities(1, false);
}, [status, isAuthenticated, fetchActivities]);
+ const loadMore = () => {
+ const nextPage = currentPage + 1;
+ setCurrentPage(nextPage);
+ fetchActivities(nextPage, true);
+ };
+
+ const hasMore = currentPage < pagination.totalPages;
+
if (status === "loading") {
return ;
}
@@ -81,6 +121,18 @@ export default function ActivityPage() {
+ {!loading && hasMore && (
+
+
+ {loadingMore ? "Loading..." : "Load More"}
+
+
+ )}
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 76022de..d9ded4c 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -15,7 +15,11 @@ export default function DashboardPage() {
handleUploadSuccess,
handleFileAction,
handleNewClick,
- } = useDashboardPage({ endpoint: "/api/files" });
+ pagination,
+ goToNextPage,
+ goToPreviousPage,
+ goToPage,
+ } = useDashboardPage({ endpoint: "/api/files", pageSize: 18 });
if (status === "loading") {
return ;
@@ -36,6 +40,10 @@ export default function DashboardPage() {
onFileAction={handleFileAction}
onNewClick={handleNewClick}
loadingMessage="Loading files..."
+ pagination={pagination}
+ onNextPage={goToNextPage}
+ onPreviousPage={goToPreviousPage}
+ onGoToPage={goToPage}
/>
);
}
diff --git a/app/dashboard/recent/page.tsx b/app/dashboard/recent/page.tsx
index 94ba4d2..f90a005 100644
--- a/app/dashboard/recent/page.tsx
+++ b/app/dashboard/recent/page.tsx
@@ -15,7 +15,11 @@ export default function RecentPage() {
handleUploadSuccess,
handleFileAction,
handleNewClick,
- } = useDashboardPage({ endpoint: "/api/files/recent" });
+ pagination,
+ goToNextPage,
+ goToPreviousPage,
+ goToPage,
+ } = useDashboardPage({ endpoint: "/api/files/recent", pageSize: 12 });
if (status === "loading") {
return ;
@@ -36,6 +40,10 @@ export default function RecentPage() {
onFileAction={handleFileAction}
onNewClick={handleNewClick}
loadingMessage="Loading recent files..."
+ pagination={pagination}
+ onNextPage={goToNextPage}
+ onPreviousPage={goToPreviousPage}
+ onGoToPage={goToPage}
/>
);
}
diff --git a/app/dashboard/starred/page.tsx b/app/dashboard/starred/page.tsx
index 6c31686..eedcf5f 100644
--- a/app/dashboard/starred/page.tsx
+++ b/app/dashboard/starred/page.tsx
@@ -15,7 +15,11 @@ export default function StarredPage() {
handleUploadSuccess,
handleFileAction,
handleNewClick,
- } = useDashboardPage({ endpoint: "/api/files/starred" });
+ pagination,
+ goToNextPage,
+ goToPreviousPage,
+ goToPage,
+ } = useDashboardPage({ endpoint: "/api/files/starred", pageSize: 12 });
if (status === "loading") {
return ;
@@ -36,6 +40,10 @@ export default function StarredPage() {
onFileAction={handleFileAction}
onNewClick={handleNewClick}
loadingMessage="Loading starred files..."
+ pagination={pagination}
+ onNextPage={goToNextPage}
+ onPreviousPage={goToPreviousPage}
+ onGoToPage={goToPage}
/>
);
}
diff --git a/app/dashboard/trash/page.tsx b/app/dashboard/trash/page.tsx
index 45be539..241bb57 100644
--- a/app/dashboard/trash/page.tsx
+++ b/app/dashboard/trash/page.tsx
@@ -15,7 +15,11 @@ export default function TrashPage() {
handleUploadSuccess,
handleFileAction,
handleNewClick,
- } = useDashboardPage({ endpoint: "/api/files/trash" });
+ pagination,
+ goToNextPage,
+ goToPreviousPage,
+ goToPage,
+ } = useDashboardPage({ endpoint: "/api/files/trash", pageSize: 25 });
if (status === "loading") {
return ;
@@ -36,6 +40,10 @@ export default function TrashPage() {
onFileAction={handleFileAction}
onNewClick={handleNewClick}
loadingMessage="Loading trash files..."
+ pagination={pagination}
+ onNextPage={goToNextPage}
+ onPreviousPage={goToPreviousPage}
+ onGoToPage={goToPage}
/>
);
}
diff --git a/components/common/PaginationControls.tsx b/components/common/PaginationControls.tsx
new file mode 100644
index 0000000..a4f322e
--- /dev/null
+++ b/components/common/PaginationControls.tsx
@@ -0,0 +1,115 @@
+import { Button } from "@/components/ui/button";
+import { ChevronLeft, ChevronRight } from "lucide-react";
+
+interface PaginationControlsProps {
+ currentPage: number;
+ totalPages: number;
+ totalCount: number;
+ pageSize: number;
+ onPreviousPage: () => void;
+ onNextPage: () => void;
+ onGoToPage: (page: number) => void;
+}
+
+export function PaginationControls({
+ currentPage,
+ totalPages,
+ totalCount,
+ pageSize,
+ onPreviousPage,
+ onNextPage,
+ onGoToPage,
+}: PaginationControlsProps) {
+ if (totalCount === 0) {
+ return null;
+ }
+
+ const startItem = (currentPage - 1) * pageSize + 1;
+ const endItem = Math.min(currentPage * pageSize, totalCount);
+
+ const getPageNumbers = () => {
+ const pages: (number | string)[] = [];
+ const maxVisiblePages = 5;
+
+ if (totalPages <= maxVisiblePages) {
+ for (let i = 1; i <= totalPages; i++) {
+ pages.push(i);
+ }
+ } else {
+ if (currentPage <= 3) {
+ pages.push(1, 2, 3, 4, "...", totalPages);
+ } else if (currentPage >= totalPages - 2) {
+ pages.push(
+ 1,
+ "...",
+ totalPages - 3,
+ totalPages - 2,
+ totalPages - 1,
+ totalPages
+ );
+ } else {
+ pages.push(
+ 1,
+ "...",
+ currentPage - 1,
+ currentPage,
+ currentPage + 1,
+ "...",
+ totalPages
+ );
+ }
+ }
+
+ return pages;
+ };
+
+ return (
+
+
+ Showing {startItem}-{endItem} of {totalCount}
+
+
+
+
+
+ Previous
+
+
+
+ {getPageNumbers().map((page, index) =>
+ page === "..." ? (
+
+ ...
+
+ ) : (
+ onGoToPage(page as number)}
+ className="min-w-[36px]"
+ >
+ {page}
+
+ )
+ )}
+
+
+
+ Next
+
+
+
+
+ );
+}
diff --git a/components/layout/DashboardLayout.tsx b/components/layout/DashboardLayout.tsx
index b67f9cc..f463ab7 100644
--- a/components/layout/DashboardLayout.tsx
+++ b/components/layout/DashboardLayout.tsx
@@ -5,8 +5,16 @@ import { DashboardHeader } from "@/components/layout/DashboardHeader";
import { Sidebar } from "@/components/layout/SidebarComponent";
import { FileDisplay } from "@/components/files/FileDisplay";
import { UploadModal } from "@/components/modals/UploadModal";
+import { PaginationControls } from "@/components/common/PaginationControls";
import type { FileDocument } from "@/types";
+interface PaginationMetadata {
+ currentPage: number;
+ totalPages: number;
+ totalCount: number;
+ limit: number;
+}
+
interface DashboardLayoutProps {
files: FileDocument[];
loading: boolean;
@@ -17,6 +25,10 @@ interface DashboardLayoutProps {
onFileAction: () => void;
onNewClick: () => void;
loadingMessage?: string;
+ pagination?: PaginationMetadata;
+ onNextPage?: () => void;
+ onPreviousPage?: () => void;
+ onGoToPage?: (page: number) => void;
}
export function DashboardLayout({
@@ -29,6 +41,10 @@ export function DashboardLayout({
onFileAction,
onNewClick,
loadingMessage = "Loading files...",
+ pagination,
+ onNextPage,
+ onPreviousPage,
+ onGoToPage,
}: DashboardLayoutProps) {
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
@@ -71,12 +87,25 @@ export function DashboardLayout({
{loadingMessage}
) : (
-
+ <>
+
+ {pagination && onNextPage && onPreviousPage && onGoToPage && (
+
+ )}
+ >
)}
diff --git a/hooks/dashboard/useDashboardPage.ts b/hooks/dashboard/useDashboardPage.ts
index 86b6d27..3253bc1 100644
--- a/hooks/dashboard/useDashboardPage.ts
+++ b/hooks/dashboard/useDashboardPage.ts
@@ -5,44 +5,71 @@ import type { FileDocument } from "@/types";
interface UseDashboardPageOptions {
endpoint: string;
+ pageSize: number;
}
-export function useDashboardPage({ endpoint }: UseDashboardPageOptions) {
+interface PaginationMetadata {
+ currentPage: number;
+ totalPages: number;
+ totalCount: number;
+ limit: number;
+}
+
+export function useDashboardPage({
+ endpoint,
+ pageSize,
+}: UseDashboardPageOptions) {
const { session, status, isAuthenticated } = useRequireAuth();
const { refreshStorage } = useStorage();
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(true);
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pagination, setPagination] = useState({
+ currentPage: 1,
+ totalPages: 1,
+ totalCount: 0,
+ limit: pageSize,
+ });
- const fetchFiles = useCallback(async () => {
- try {
- const response = await fetch(endpoint);
- if (response.ok) {
- const data = await response.json();
- setFiles(data.files);
+ const fetchFiles = useCallback(
+ async (page: number = currentPage) => {
+ setLoading(true);
+ try {
+ const url = `${endpoint}?page=${page}&limit=${pageSize}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ const data = await response.json();
+ setFiles(data.files);
+ if (data.pagination) {
+ setPagination(data.pagination);
+ }
+ }
+ } catch (error) {
+ console.error("Failed to fetch files:", error);
+ } finally {
+ setLoading(false);
}
- } catch (error) {
- console.error("Failed to fetch files:", error);
- } finally {
- setLoading(false);
- }
- }, [endpoint]);
+ },
+ [endpoint, pageSize, currentPage]
+ );
useEffect(() => {
if (status === "loading" || !isAuthenticated) {
return;
}
- fetchFiles();
- }, [status, isAuthenticated, fetchFiles]);
+ fetchFiles(currentPage);
+ }, [status, isAuthenticated, currentPage, fetchFiles]);
const handleUploadSuccess = async () => {
- await fetchFiles();
+ setCurrentPage(1);
+ await fetchFiles(1);
await refreshStorage();
};
const handleFileAction = async () => {
- await fetchFiles();
+ await fetchFiles(currentPage);
await refreshStorage();
};
@@ -50,6 +77,24 @@ export function useDashboardPage({ endpoint }: UseDashboardPageOptions) {
setIsUploadModalOpen(true);
};
+ const goToNextPage = () => {
+ if (currentPage < pagination.totalPages) {
+ setCurrentPage(prev => prev + 1);
+ }
+ };
+
+ const goToPreviousPage = () => {
+ if (currentPage > 1) {
+ setCurrentPage(prev => prev - 1);
+ }
+ };
+
+ const goToPage = (page: number) => {
+ if (page >= 1 && page <= pagination.totalPages) {
+ setCurrentPage(page);
+ }
+ };
+
return {
session,
status,
@@ -60,5 +105,9 @@ export function useDashboardPage({ endpoint }: UseDashboardPageOptions) {
handleUploadSuccess,
handleFileAction,
handleNewClick,
+ pagination,
+ goToNextPage,
+ goToPreviousPage,
+ goToPage,
};
}
From 616fefb2be0d36dce2429a721364794c415a5e77 Mon Sep 17 00:00:00 2001
From: Abul
Date: Tue, 9 Dec 2025 00:32:59 +0530
Subject: [PATCH 04/14] update
---
package-lock.json | 2561 +++++++++++++++++++++++++++++++--------------
package.json | 4 +-
2 files changed, 1776 insertions(+), 789 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 1f6d6e6..3c33d12 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
"csrf": "^3.1.0",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
- "next": "15.5.2",
+ "next": "16.0.7",
"next-auth": "^4.24.11",
"next-themes": "^0.4.6",
"react": "19.1.0",
@@ -37,7 +37,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "15.5.2",
+ "eslint-config-next": "16.0.7",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"tailwindcss": "^4",
@@ -71,13 +71,13 @@
}
},
"node_modules/@azure/core-auth": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz",
- "integrity": "sha512-88Djs5vBvGbHQHf5ZZcaoNHo6Y8BKZkt3cw2iuJIQzLEgH4Ox6Tm4hjFhbqOxyYsgIG/eJbFEHpxRIfEEWv5Ow==",
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz",
+ "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==",
"license": "MIT",
"dependencies": {
- "@azure/abort-controller": "^2.0.0",
- "@azure/core-util": "^1.11.0",
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-util": "^1.13.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -85,17 +85,17 @@
}
},
"node_modules/@azure/core-client": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.0.tgz",
- "integrity": "sha512-O4aP3CLFNodg8eTHXECaH3B3CjicfzkxVtnrfLkOq0XNP7TIECGfHpK/C6vADZkWP75wzmdBnsIA8ksuJMk18g==",
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz",
+ "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==",
"license": "MIT",
"dependencies": {
- "@azure/abort-controller": "^2.0.0",
- "@azure/core-auth": "^1.4.0",
- "@azure/core-rest-pipeline": "^1.20.0",
- "@azure/core-tracing": "^1.0.0",
- "@azure/core-util": "^1.6.1",
- "@azure/logger": "^1.0.0",
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
"tslib": "^2.6.2"
},
"engines": {
@@ -103,17 +103,17 @@
}
},
"node_modules/@azure/core-http-compat": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz",
- "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz",
+ "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==",
"license": "MIT",
"dependencies": {
- "@azure/abort-controller": "^2.0.0",
- "@azure/core-client": "^1.3.0",
- "@azure/core-rest-pipeline": "^1.20.0"
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-client": "^1.10.0",
+ "@azure/core-rest-pipeline": "^1.22.0"
},
"engines": {
- "node": ">=18.0.0"
+ "node": ">=20.0.0"
}
},
"node_modules/@azure/core-lro": {
@@ -144,16 +144,16 @@
}
},
"node_modules/@azure/core-rest-pipeline": {
- "version": "1.22.0",
- "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.0.tgz",
- "integrity": "sha512-OKHmb3/Kpm06HypvB3g6Q3zJuvyXcpxDpCS1PnU8OV6AJgSFaee/covXBcPbWc6XDDxtEPlbi3EMQ6nUiPaQtw==",
+ "version": "1.22.2",
+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz",
+ "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==",
"license": "MIT",
"dependencies": {
- "@azure/abort-controller": "^2.0.0",
- "@azure/core-auth": "^1.8.0",
- "@azure/core-tracing": "^1.0.1",
- "@azure/core-util": "^1.11.0",
- "@azure/logger": "^1.0.0",
+ "@azure/abort-controller": "^2.1.2",
+ "@azure/core-auth": "^1.10.0",
+ "@azure/core-tracing": "^1.3.0",
+ "@azure/core-util": "^1.13.0",
+ "@azure/logger": "^1.3.0",
"@typespec/ts-http-runtime": "^0.3.0",
"tslib": "^2.6.2"
},
@@ -162,9 +162,9 @@
}
},
"node_modules/@azure/core-tracing": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.0.tgz",
- "integrity": "sha512-+XvmZLLWPe67WXNZo9Oc9CrPj/Tm8QnHR92fFAFdnbzwNdCH1h+7UdpaQgRSBsMY+oW1kHXNUZQLdZ1gHX3ROw==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz",
+ "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.6.2"
@@ -174,12 +174,12 @@
}
},
"node_modules/@azure/core-util": {
- "version": "1.13.0",
- "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.0.tgz",
- "integrity": "sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz",
+ "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==",
"license": "MIT",
"dependencies": {
- "@azure/abort-controller": "^2.0.0",
+ "@azure/abort-controller": "^2.1.2",
"@typespec/ts-http-runtime": "^0.3.0",
"tslib": "^2.6.2"
},
@@ -214,9 +214,9 @@
}
},
"node_modules/@azure/storage-blob": {
- "version": "12.28.0",
- "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.28.0.tgz",
- "integrity": "sha512-VhQHITXXO03SURhDiGuHhvc/k/sD2WvJUS7hqhiVNbErVCuQoLtWql7r97fleBlIRKHJaa9R7DpBjfE0pfLYcA==",
+ "version": "12.29.1",
+ "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.29.1.tgz",
+ "integrity": "sha512-7ktyY0rfTM0vo7HvtK6E3UvYnI9qfd6Oz6z/+92VhGRveWng3kJwMKeUpqmW/NmwcDNbxHpSlldG+vsUnRFnBg==",
"license": "MIT",
"dependencies": {
"@azure/abort-controller": "^2.1.2",
@@ -230,7 +230,7 @@
"@azure/core-util": "^1.11.0",
"@azure/core-xml": "^1.4.5",
"@azure/logger": "^1.1.4",
- "@azure/storage-common": "^12.0.0-beta.2",
+ "@azure/storage-common": "^12.1.1",
"events": "^3.0.0",
"tslib": "^2.8.1"
},
@@ -239,9 +239,9 @@
}
},
"node_modules/@azure/storage-common": {
- "version": "12.0.0",
- "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.0.0.tgz",
- "integrity": "sha512-QyEWXgi4kdRo0wc1rHum9/KnaWZKCdQGZK1BjU4fFL6Jtedp7KLbQihgTTVxldFy1z1ZPtuDPx8mQ5l3huPPbA==",
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.1.1.tgz",
+ "integrity": "sha512-eIOH1pqFwI6UmVNnDQvmFeSg0XppuzDLFeUNO/Xht7ODAzRLgGDh7h550pSxoA+lPDxBl1+D2m/KG3jWzCUjTg==",
"license": "MIT",
"dependencies": {
"@azure/abort-controller": "^2.1.2",
@@ -258,19 +258,259 @@
"node": ">=20.0.0"
}
},
- "node_modules/@babel/runtime": {
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
"version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
- "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
"license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@emnapi/core": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
- "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -280,9 +520,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
- "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
+ "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -301,9 +541,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz",
- "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==",
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -333,9 +573,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -343,13 +583,13 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
+ "@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
@@ -358,19 +598,22 @@
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -381,9 +624,9 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -393,7 +636,7 @@
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
+ "js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
@@ -405,9 +648,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz",
- "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==",
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -418,9 +661,9 @@
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -428,13 +671,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
@@ -531,10 +774,20 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@img/sharp-darwin-arm64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
- "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
"cpu": [
"arm64"
],
@@ -550,13 +803,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.2.0"
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
- "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
"cpu": [
"x64"
],
@@ -572,13 +825,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.2.0"
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
- "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
"cpu": [
"arm64"
],
@@ -592,9 +845,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
- "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
"cpu": [
"x64"
],
@@ -608,9 +861,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
- "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
"cpu": [
"arm"
],
@@ -624,9 +877,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
- "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
"cpu": [
"arm64"
],
@@ -640,9 +893,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
- "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
"cpu": [
"ppc64"
],
@@ -655,10 +908,26 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
- "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
"cpu": [
"s390x"
],
@@ -672,9 +941,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
- "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
"cpu": [
"x64"
],
@@ -688,9 +957,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
- "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
"cpu": [
"arm64"
],
@@ -704,9 +973,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
- "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
"cpu": [
"x64"
],
@@ -720,9 +989,9 @@
}
},
"node_modules/@img/sharp-linux-arm": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
- "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
"cpu": [
"arm"
],
@@ -738,13 +1007,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.2.0"
+ "@img/sharp-libvips-linux-arm": "1.2.4"
}
},
"node_modules/@img/sharp-linux-arm64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
- "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
"cpu": [
"arm64"
],
@@ -760,13 +1029,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.2.0"
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-ppc64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
- "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
"cpu": [
"ppc64"
],
@@ -782,13 +1051,35 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-ppc64": "1.2.0"
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-s390x": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
- "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
"cpu": [
"s390x"
],
@@ -804,13 +1095,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.2.0"
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
}
},
"node_modules/@img/sharp-linux-x64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
- "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
@@ -826,13 +1117,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.2.0"
+ "@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
- "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
"cpu": [
"arm64"
],
@@ -848,13 +1139,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
- "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
"x64"
],
@@ -870,20 +1161,20 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.2.0"
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},
"node_modules/@img/sharp-wasm32": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
- "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
"cpu": [
"wasm32"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
- "@emnapi/runtime": "^1.4.4"
+ "@emnapi/runtime": "^1.7.0"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -893,9 +1184,9 @@
}
},
"node_modules/@img/sharp-win32-arm64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
- "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
"cpu": [
"arm64"
],
@@ -912,9 +1203,9 @@
}
},
"node_modules/@img/sharp-win32-ia32": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
- "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
"cpu": [
"ia32"
],
@@ -931,9 +1222,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
- "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
"cpu": [
"x64"
],
@@ -949,19 +1240,6 @@
"url": "https://opencollective.com/libvips"
}
},
- "node_modules/@isaacs/fs-minipass": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
- "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.4"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1002,9 +1280,9 @@
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.30",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
- "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1013,9 +1291,9 @@
}
},
"node_modules/@mongodb-js/saslprep": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
- "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz",
+ "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==",
"license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
@@ -1035,15 +1313,15 @@
}
},
"node_modules/@next/env": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz",
- "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz",
+ "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.2.tgz",
- "integrity": "sha512-lkLrRVxcftuOsJNhWatf1P2hNVfh98k/omQHrCEPPriUypR6RcS13IvLdIrEvkm9AH2Nu2YpR5vLqBuy6twH3Q==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.7.tgz",
+ "integrity": "sha512-hFrTNZcMEG+k7qxVxZJq3F32Kms130FAhG8lvw2zkKBgAcNOJIxlljNiCjGygvBshvaGBdf88q2CqWtnqezDHA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1051,9 +1329,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz",
- "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz",
+ "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==",
"cpu": [
"arm64"
],
@@ -1067,9 +1345,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz",
- "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz",
+ "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==",
"cpu": [
"x64"
],
@@ -1083,9 +1361,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz",
- "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz",
+ "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==",
"cpu": [
"arm64"
],
@@ -1099,9 +1377,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz",
- "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz",
+ "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==",
"cpu": [
"arm64"
],
@@ -1115,9 +1393,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz",
- "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz",
+ "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==",
"cpu": [
"x64"
],
@@ -1131,9 +1409,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz",
- "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz",
+ "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==",
"cpu": [
"x64"
],
@@ -1147,9 +1425,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz",
- "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz",
+ "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==",
"cpu": [
"arm64"
],
@@ -1163,9 +1441,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz",
- "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz",
+ "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==",
"cpu": [
"x64"
],
@@ -1264,14 +1542,55 @@
}
}
},
+ "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-avatar": {
- "version": "1.1.10",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
- "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz",
+ "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-context": "1.1.3",
+ "@radix-ui/react-primitive": "2.1.4",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-is-hydrated": "0.1.0",
"@radix-ui/react-use-layout-effect": "1.1.1"
@@ -1317,10 +1636,10 @@
}
}
},
- "node_modules/@radix-ui/react-compose-refs": {
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
- "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -1332,10 +1651,66 @@
}
}
},
- "node_modules/@radix-ui/react-context": {
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
- "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz",
+ "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
@@ -1383,6 +1758,62 @@
}
}
},
+ "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@@ -1425,6 +1856,47 @@
}
}
},
+ "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-dropdown-menu": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz",
@@ -1454,6 +1926,62 @@
}
}
},
+ "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
@@ -1494,6 +2022,47 @@
}
}
},
+ "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
@@ -1552,6 +2121,62 @@
}
}
},
+ "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -1584,6 +2209,62 @@
}
}
},
+ "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
@@ -1608,6 +2289,47 @@
}
}
},
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
@@ -1633,12 +2355,12 @@
}
},
"node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
+ "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/react-slot": "1.2.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -1656,13 +2378,13 @@
}
},
"node_modules/@radix-ui/react-progress": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz",
- "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==",
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz",
+ "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3"
+ "@radix-ui/react-context": "1.1.3",
+ "@radix-ui/react-primitive": "2.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -1710,7 +2432,45 @@
}
}
},
- "node_modules/@radix-ui/react-slot": {
+ "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
@@ -1728,6 +2488,24 @@
}
}
},
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-tooltip": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
@@ -1762,6 +2540,62 @@
}
}
},
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@@ -1924,6 +2758,47 @@
}
}
},
+ "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
@@ -1937,13 +2812,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@rushstack/eslint-patch": {
- "version": "1.12.0",
- "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz",
- "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -1954,54 +2822,49 @@
}
},
"node_modules/@tailwindcss/node": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
- "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
+ "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"enhanced-resolve": "^5.18.3",
- "jiti": "^2.5.1",
- "lightningcss": "1.30.1",
- "magic-string": "^0.30.18",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
- "tailwindcss": "4.1.13"
+ "tailwindcss": "4.1.17"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz",
- "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz",
+ "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==",
"dev": true,
- "hasInstallScript": true,
"license": "MIT",
- "dependencies": {
- "detect-libc": "^2.0.4",
- "tar": "^7.4.3"
- },
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.1.13",
- "@tailwindcss/oxide-darwin-arm64": "4.1.13",
- "@tailwindcss/oxide-darwin-x64": "4.1.13",
- "@tailwindcss/oxide-freebsd-x64": "4.1.13",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13",
- "@tailwindcss/oxide-linux-arm64-musl": "4.1.13",
- "@tailwindcss/oxide-linux-x64-gnu": "4.1.13",
- "@tailwindcss/oxide-linux-x64-musl": "4.1.13",
- "@tailwindcss/oxide-wasm32-wasi": "4.1.13",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13",
- "@tailwindcss/oxide-win32-x64-msvc": "4.1.13"
+ "@tailwindcss/oxide-android-arm64": "4.1.17",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.17",
+ "@tailwindcss/oxide-darwin-x64": "4.1.17",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.17",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.17",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.17",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.17",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.17",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.17"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz",
- "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz",
+ "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==",
"cpu": [
"arm64"
],
@@ -2016,9 +2879,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz",
- "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz",
+ "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==",
"cpu": [
"arm64"
],
@@ -2033,9 +2896,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz",
- "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz",
+ "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==",
"cpu": [
"x64"
],
@@ -2050,9 +2913,9 @@
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz",
- "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz",
+ "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==",
"cpu": [
"x64"
],
@@ -2067,9 +2930,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz",
- "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz",
+ "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==",
"cpu": [
"arm"
],
@@ -2084,9 +2947,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz",
- "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz",
+ "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==",
"cpu": [
"arm64"
],
@@ -2101,9 +2964,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz",
- "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz",
+ "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==",
"cpu": [
"arm64"
],
@@ -2118,9 +2981,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz",
- "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz",
+ "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==",
"cpu": [
"x64"
],
@@ -2135,9 +2998,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz",
- "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz",
+ "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==",
"cpu": [
"x64"
],
@@ -2152,9 +3015,9 @@
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz",
- "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz",
+ "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
@@ -2170,21 +3033,21 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "^1.4.5",
- "@emnapi/runtime": "^1.4.5",
- "@emnapi/wasi-threads": "^1.0.4",
- "@napi-rs/wasm-runtime": "^0.2.12",
- "@tybys/wasm-util": "^0.10.0",
- "tslib": "^2.8.0"
+ "@emnapi/core": "^1.6.0",
+ "@emnapi/runtime": "^1.6.0",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.0.7",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz",
- "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz",
+ "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==",
"cpu": [
"arm64"
],
@@ -2199,9 +3062,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz",
- "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz",
+ "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==",
"cpu": [
"x64"
],
@@ -2216,23 +3079,23 @@
}
},
"node_modules/@tailwindcss/postcss": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz",
- "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz",
+ "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
- "@tailwindcss/node": "4.1.13",
- "@tailwindcss/oxide": "4.1.13",
+ "@tailwindcss/node": "4.1.17",
+ "@tailwindcss/oxide": "4.1.17",
"postcss": "^8.4.41",
- "tailwindcss": "4.1.13"
+ "tailwindcss": "4.1.17"
}
},
"node_modules/@tybys/wasm-util": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
- "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -2269,9 +3132,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.19.13",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz",
- "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==",
+ "version": "20.19.25",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
+ "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2279,23 +3142,23 @@
}
},
"node_modules/@types/react": {
- "version": "19.1.12",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
- "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"devOptional": true,
"license": "MIT",
"dependencies": {
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
- "@types/react": "^19.0.0"
+ "@types/react": "^19.2.0"
}
},
"node_modules/@types/webidl-conversions": {
@@ -2314,18 +3177,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
- "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz",
+ "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/type-utils": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
- "graphemer": "^1.4.0",
+ "@typescript-eslint/scope-manager": "8.49.0",
+ "@typescript-eslint/type-utils": "8.49.0",
+ "@typescript-eslint/utils": "8.49.0",
+ "@typescript-eslint/visitor-keys": "8.49.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
@@ -2338,7 +3200,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.42.0",
+ "@typescript-eslint/parser": "^8.49.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -2354,16 +3216,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz",
- "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz",
+ "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
+ "@typescript-eslint/scope-manager": "8.49.0",
+ "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/typescript-estree": "8.49.0",
+ "@typescript-eslint/visitor-keys": "8.49.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2379,14 +3241,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
- "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz",
+ "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.42.0",
- "@typescript-eslint/types": "^8.42.0",
+ "@typescript-eslint/tsconfig-utils": "^8.49.0",
+ "@typescript-eslint/types": "^8.49.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2401,14 +3263,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
- "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz",
+ "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0"
+ "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/visitor-keys": "8.49.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2419,9 +3281,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
- "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz",
+ "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2436,15 +3298,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz",
- "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz",
+ "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0",
- "@typescript-eslint/utils": "8.42.0",
+ "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/typescript-estree": "8.49.0",
+ "@typescript-eslint/utils": "8.49.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -2461,9 +3323,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
- "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz",
+ "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2475,21 +3337,20 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
- "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz",
+ "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.42.0",
- "@typescript-eslint/tsconfig-utils": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/visitor-keys": "8.42.0",
+ "@typescript-eslint/project-service": "8.49.0",
+ "@typescript-eslint/tsconfig-utils": "8.49.0",
+ "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/visitor-keys": "8.49.0",
"debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
"ts-api-utils": "^2.1.0"
},
"engines": {
@@ -2513,36 +3374,6 @@
"balanced-match": "^1.0.0"
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "license": "MIT",
- "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"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -2559,17 +3390,30 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@typescript-eslint/utils": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz",
- "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz",
+ "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.42.0",
- "@typescript-eslint/types": "8.42.0",
- "@typescript-eslint/typescript-estree": "8.42.0"
+ "@typescript-eslint/scope-manager": "8.49.0",
+ "@typescript-eslint/types": "8.49.0",
+ "@typescript-eslint/typescript-estree": "8.49.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2584,13 +3428,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.42.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
- "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz",
+ "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.42.0",
+ "@typescript-eslint/types": "8.49.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2602,9 +3446,9 @@
}
},
"node_modules/@typespec/ts-http-runtime": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz",
- "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz",
+ "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==",
"license": "MIT",
"dependencies": {
"http-proxy-agent": "^7.0.0",
@@ -3172,9 +4016,9 @@
}
},
"node_modules/axe-core": {
- "version": "4.10.3",
- "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
- "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz",
+ "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==",
"dev": true,
"license": "MPL-2.0",
"engines": {
@@ -3198,6 +4042,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz",
+ "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -3222,6 +4076,40 @@
"node": ">=8"
}
},
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/bson": {
"version": "6.10.4",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
@@ -3292,9 +4180,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001739",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
- "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
+ "version": "1.0.30001759",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz",
+ "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==",
"funding": [
{
"type": "opencollective",
@@ -3328,16 +4216,6 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/class-variance-authority": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
@@ -3365,25 +4243,11 @@
"node": ">=6"
}
},
- "node_modules/color": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-convert": "^2.0.1",
- "color-string": "^1.9.0"
- },
- "engines": {
- "node": ">=12.5.0"
- }
- },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -3396,20 +4260,9 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT"
},
- "node_modules/color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
- },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3417,6 +4270,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@@ -3456,9 +4316,9 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"devOptional": true,
"license": "MIT"
},
@@ -3524,9 +4384,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -3584,9 +4444,9 @@
}
},
"node_modules/detect-libc": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
- "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"devOptional": true,
"license": "Apache-2.0",
"engines": {
@@ -3627,6 +4487,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.266",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz",
+ "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -3825,6 +4692,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3839,25 +4716,24 @@
}
},
"node_modules/eslint": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz",
- "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.34.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
"@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",
@@ -3900,25 +4776,24 @@
}
},
"node_modules/eslint-config-next": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.2.tgz",
- "integrity": "sha512-3hPZghsLupMxxZ2ggjIIrat/bPniM2yRpsVPVM40rp8ZMzKWOJp2CGWn7+EzoV2ddkUr5fxNfHpF+wU1hGt/3g==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.7.tgz",
+ "integrity": "sha512-WubFGLFHfk2KivkdRGfx6cGSFhaQqhERRfyO8BRx+qiGPGp7WLKcPvYC4mdx1z3VhVRcrfFzczjjTrbJZOpnEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "15.5.2",
- "@rushstack/eslint-patch": "^1.10.3",
- "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
- "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "@next/eslint-plugin-next": "16.0.7",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
- "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-react": "^7.37.0",
- "eslint-plugin-react-hooks": "^5.0.0"
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "globals": "16.4.0",
+ "typescript-eslint": "^8.46.0"
},
"peerDependencies": {
- "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0",
+ "eslint": ">=9.0.0",
"typescript": ">=3.3.1"
},
"peerDependenciesMeta": {
@@ -3927,6 +4802,19 @@
}
}
},
+ "node_modules/eslint-config-next/node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/eslint-config-prettier": {
"version": "10.1.8",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
@@ -4072,16 +4960,6 @@
"ms": "^2.1.1"
}
},
- "node_modules/eslint-plugin-import/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
@@ -4146,13 +5024,20 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
@@ -4176,16 +5061,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/eslint-plugin-react/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
"node_modules/eslint-scope": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
@@ -4341,9 +5216,9 @@
"license": "MIT"
},
"node_modules/fast-xml-parser": {
- "version": "5.2.5",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
- "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.2.tgz",
+ "integrity": "sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA==",
"funding": [
{
"type": "github",
@@ -4489,6 +5364,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -4556,9 +5451,9 @@
}
},
"node_modules/get-tsconfig": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
- "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4631,13 +5526,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/has-bigints": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@@ -4732,6 +5620,23 @@
"node": ">= 0.4"
}
},
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
@@ -4828,13 +5733,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "license": "MIT",
- "optional": true
- },
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -4898,6 +5796,19 @@
"semver": "^7.7.1"
}
},
+ "node_modules/is-bun-module/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -4989,14 +5900,15 @@
}
},
"node_modules/is-generator-function": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
- "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bound": "^1.0.3",
- "get-proto": "^1.0.0",
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
"has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0"
},
@@ -5251,9 +6163,9 @@
}
},
"node_modules/jiti": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
- "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -5277,9 +6189,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5289,6 +6201,19 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -5311,16 +6236,16 @@
"license": "MIT"
},
"node_modules/json5": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
- "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "minimist": "^1.2.0"
- },
"bin": {
"json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
}
},
"node_modules/jsx-ast-utils": {
@@ -5384,9 +6309,9 @@
}
},
"node_modules/lightningcss": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
- "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
@@ -5400,22 +6325,44 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "lightningcss-darwin-arm64": "1.30.1",
- "lightningcss-darwin-x64": "1.30.1",
- "lightningcss-freebsd-x64": "1.30.1",
- "lightningcss-linux-arm-gnueabihf": "1.30.1",
- "lightningcss-linux-arm64-gnu": "1.30.1",
- "lightningcss-linux-arm64-musl": "1.30.1",
- "lightningcss-linux-x64-gnu": "1.30.1",
- "lightningcss-linux-x64-musl": "1.30.1",
- "lightningcss-win32-arm64-msvc": "1.30.1",
- "lightningcss-win32-x64-msvc": "1.30.1"
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
- "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
"cpu": [
"arm64"
],
@@ -5434,9 +6381,9 @@
}
},
"node_modules/lightningcss-darwin-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
- "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
"cpu": [
"x64"
],
@@ -5455,9 +6402,9 @@
}
},
"node_modules/lightningcss-freebsd-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
- "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
"cpu": [
"x64"
],
@@ -5476,9 +6423,9 @@
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
- "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
"cpu": [
"arm"
],
@@ -5497,9 +6444,9 @@
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
- "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
"cpu": [
"arm64"
],
@@ -5518,9 +6465,9 @@
}
},
"node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
- "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
"cpu": [
"arm64"
],
@@ -5539,9 +6486,9 @@
}
},
"node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
- "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
"cpu": [
"x64"
],
@@ -5560,9 +6507,9 @@
}
},
"node_modules/lightningcss-linux-x64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
- "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
"cpu": [
"x64"
],
@@ -5581,9 +6528,9 @@
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
- "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
"cpu": [
"arm64"
],
@@ -5602,9 +6549,9 @@
}
},
"node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
- "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
"cpu": [
"x64"
],
@@ -5659,23 +6606,15 @@
}
},
"node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
"license": "ISC",
"dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
+ "yallist": "^3.0.2"
}
},
- "node_modules/lru-cache/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "license": "ISC"
- },
"node_modules/lucide-react": {
"version": "0.542.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz",
@@ -5686,9 +6625,9 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.18",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
- "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5758,54 +6697,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minizlib": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
- "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/mkdirp": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
- "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "mkdirp": "dist/cjs/src/bin.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/mongodb": {
- "version": "6.19.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.19.0.tgz",
- "integrity": "sha512-H3GtYujOJdeKIMLKBT9PwlDhGrQfplABNF1G904w6r5ZXKWyv77aB0X9B+rhmaAwjtllHzaEkvi9mkGVZxs2Bw==",
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz",
+ "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==",
"license": "Apache-2.0",
"dependencies": {
- "@mongodb-js/saslprep": "^1.1.9",
+ "@mongodb-js/saslprep": "^1.3.0",
"bson": "^6.10.4",
- "mongodb-connection-string-url": "^3.0.0"
+ "mongodb-connection-string-url": "^3.0.2"
},
"engines": {
"node": ">=16.20.1"
@@ -5878,9 +6778,9 @@
}
},
"node_modules/napi-postinstall": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz",
- "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==",
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -5901,12 +6801,12 @@
"license": "MIT"
},
"node_modules/next": {
- "version": "15.5.2",
- "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz",
- "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==",
+ "version": "16.0.7",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz",
+ "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==",
"license": "MIT",
"dependencies": {
- "@next/env": "15.5.2",
+ "@next/env": "16.0.7",
"@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
@@ -5916,18 +6816,18 @@
"next": "dist/bin/next"
},
"engines": {
- "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ "node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "15.5.2",
- "@next/swc-darwin-x64": "15.5.2",
- "@next/swc-linux-arm64-gnu": "15.5.2",
- "@next/swc-linux-arm64-musl": "15.5.2",
- "@next/swc-linux-x64-gnu": "15.5.2",
- "@next/swc-linux-x64-musl": "15.5.2",
- "@next/swc-win32-arm64-msvc": "15.5.2",
- "@next/swc-win32-x64-msvc": "15.5.2",
- "sharp": "^0.34.3"
+ "@next/swc-darwin-arm64": "16.0.7",
+ "@next/swc-darwin-x64": "16.0.7",
+ "@next/swc-linux-arm64-gnu": "16.0.7",
+ "@next/swc-linux-arm64-musl": "16.0.7",
+ "@next/swc-linux-x64-gnu": "16.0.7",
+ "@next/swc-linux-x64-musl": "16.0.7",
+ "@next/swc-win32-arm64-msvc": "16.0.7",
+ "@next/swc-win32-x64-msvc": "16.0.7",
+ "sharp": "^0.34.4"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -5953,9 +6853,9 @@
}
},
"node_modules/next-auth": {
- "version": "4.24.11",
- "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz",
- "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==",
+ "version": "4.24.13",
+ "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.13.tgz",
+ "integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==",
"license": "ISC",
"dependencies": {
"@babel/runtime": "^7.20.13",
@@ -5969,9 +6869,9 @@
"uuid": "^8.3.2"
},
"peerDependencies": {
- "@auth/core": "0.34.2",
- "next": "^12.2.5 || ^13 || ^14 || ^15",
- "nodemailer": "^6.6.5",
+ "@auth/core": "0.34.3",
+ "next": "^12.2.5 || ^13 || ^14 || ^15 || ^16",
+ "nodemailer": "^7.0.7",
"react": "^17.0.2 || ^18 || ^19",
"react-dom": "^17.0.2 || ^18 || ^19"
},
@@ -6022,6 +6922,13 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
@@ -6161,9 +7068,9 @@
}
},
"node_modules/oidc-token-hash": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz",
- "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz",
+ "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==",
"license": "MIT",
"engines": {
"node": "^10.13.0 || >=12.0.0"
@@ -6184,6 +7091,24 @@
"url": "https://github.com/sponsors/panva"
}
},
+ "node_modules/openid-client/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/openid-client/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "license": "ISC"
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -6351,9 +7276,9 @@
}
},
"node_modules/preact": {
- "version": "10.27.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.1.tgz",
- "integrity": "sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==",
+ "version": "10.28.0",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.0.tgz",
+ "integrity": "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -6383,9 +7308,9 @@
}
},
"node_modules/prettier": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
- "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
+ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
"dev": true,
"license": "MIT",
"bin": {
@@ -6484,9 +7409,9 @@
"license": "MIT"
},
"node_modules/react-remove-scroll": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
- "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
@@ -6597,13 +7522,13 @@
}
},
"node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.16.0",
+ "is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -6740,16 +7665,13 @@
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "devOptional": true,
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
}
},
"node_modules/set-function-length": {
@@ -6802,16 +7724,16 @@
}
},
"node_modules/sharp": {
- "version": "0.34.3",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
- "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"optional": true,
"dependencies": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.4",
- "semver": "^7.7.2"
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -6820,28 +7742,43 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.34.3",
- "@img/sharp-darwin-x64": "0.34.3",
- "@img/sharp-libvips-darwin-arm64": "1.2.0",
- "@img/sharp-libvips-darwin-x64": "1.2.0",
- "@img/sharp-libvips-linux-arm": "1.2.0",
- "@img/sharp-libvips-linux-arm64": "1.2.0",
- "@img/sharp-libvips-linux-ppc64": "1.2.0",
- "@img/sharp-libvips-linux-s390x": "1.2.0",
- "@img/sharp-libvips-linux-x64": "1.2.0",
- "@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
- "@img/sharp-libvips-linuxmusl-x64": "1.2.0",
- "@img/sharp-linux-arm": "0.34.3",
- "@img/sharp-linux-arm64": "0.34.3",
- "@img/sharp-linux-ppc64": "0.34.3",
- "@img/sharp-linux-s390x": "0.34.3",
- "@img/sharp-linux-x64": "0.34.3",
- "@img/sharp-linuxmusl-arm64": "0.34.3",
- "@img/sharp-linuxmusl-x64": "0.34.3",
- "@img/sharp-wasm32": "0.34.3",
- "@img/sharp-win32-arm64": "0.34.3",
- "@img/sharp-win32-ia32": "0.34.3",
- "@img/sharp-win32-x64": "0.34.3"
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/shebang-command": {
@@ -6943,16 +7880,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "is-arrayish": "^0.3.1"
- }
- },
"node_modules/sonner": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
@@ -7200,9 +8127,9 @@
}
},
"node_modules/tailwind-merge": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
- "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
"license": "MIT",
"funding": {
"type": "github",
@@ -7210,16 +8137,16 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
- "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
+ "version": "4.1.17",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
+ "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
- "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7230,33 +8157,15 @@
"url": "https://opencollective.com/webpack"
}
},
- "node_modules/tar": {
- "version": "7.4.3",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
- "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.0.1",
- "mkdirp": "^3.0.1",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/tinyglobby": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
- "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -7347,6 +8256,19 @@
"strip-bom": "^3.0.0"
}
},
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -7363,9 +8285,9 @@
}
},
"node_modules/tw-animate-css": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.8.tgz",
- "integrity": "sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
"dev": true,
"license": "MIT",
"funding": {
@@ -7464,9 +8386,9 @@
}
},
"node_modules/typescript": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -7477,6 +8399,30 @@
"node": ">=14.17"
}
},
+ "node_modules/typescript-eslint": {
+ "version": "8.49.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz",
+ "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.49.0",
+ "@typescript-eslint/parser": "8.49.0",
+ "@typescript-eslint/typescript-estree": "8.49.0",
+ "@typescript-eslint/utils": "8.49.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
@@ -7550,6 +8496,37 @@
"@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
}
},
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+ "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -7604,9 +8581,9 @@
}
},
"node_modules/use-sync-external-store": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
- "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -7759,14 +8736,11 @@
}
},
"node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
+ "license": "ISC"
},
"node_modules/yocto-queue": {
"version": "0.1.0",
@@ -7782,13 +8756,26 @@
}
},
"node_modules/zod": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
- "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
}
}
}
diff --git a/package.json b/package.json
index 9d880ca..9b78307 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,7 @@
"csrf": "^3.1.0",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
- "next": "15.5.2",
+ "next": "16.0.7",
"next-auth": "^4.24.11",
"next-themes": "^0.4.6",
"react": "19.1.0",
@@ -42,7 +42,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "15.5.2",
+ "eslint-config-next": "16.0.7",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"tailwindcss": "^4",
From e534a6363cd58f433c91e80fbe6529e4cdb82435 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sat, 13 Dec 2025 19:23:56 +0530
Subject: [PATCH 05/14] Implement file search functionality
---
app/api/files/search/route.ts | 55 ++++++++++
components/files/FileDisplay.tsx | 17 +--
components/layout/DashboardHeader.tsx | 143 +++++++++++++++++++++-----
components/layout/DashboardLayout.tsx | 25 ++++-
components/search/SearchDropdown.tsx | 128 +++++++++++++++++++++++
components/ui/badge.tsx | 3 +-
hooks/search/useSearch.ts | 97 +++++++++++++++++
package-lock.json | 106 +++++++++----------
package.json | 8 +-
tsconfig.json | 24 ++++-
10 files changed, 505 insertions(+), 101 deletions(-)
create mode 100644 app/api/files/search/route.ts
create mode 100644 components/search/SearchDropdown.tsx
create mode 100644 hooks/search/useSearch.ts
diff --git a/app/api/files/search/route.ts b/app/api/files/search/route.ts
new file mode 100644
index 0000000..c77ed40
--- /dev/null
+++ b/app/api/files/search/route.ts
@@ -0,0 +1,55 @@
+import { NextRequest } from "next/server";
+import { connectToDatabase } from "@/lib/services/mongodb";
+import {
+ getAuthenticatedUser,
+ createErrorResponse,
+ createSuccessResponse,
+} from "@/lib/api/api-helpers";
+import { COLLECTIONS, ERROR_MESSAGES } from "@/lib/config/constants";
+import type { FileDocument } from "@/types";
+
+const SEARCH_LIMIT = 8;
+
+export async function GET(request: NextRequest) {
+ try {
+ const { error, user } = await getAuthenticatedUser(request);
+ if (error) {
+ return error;
+ }
+
+ const { searchParams } = new URL(request.url);
+ const query = searchParams.get("q")?.trim();
+
+ if (!query || query.length < 2) {
+ return createSuccessResponse({ files: [] });
+ }
+
+ const { db } = await connectToDatabase();
+
+ // Escape special regex characters for safety
+ const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+
+ const files = await db
+ .collection(COLLECTIONS.FILES)
+ .find({
+ userId: user!.id,
+ deletedAt: { $exists: false },
+ originalFileName: { $regex: escapedQuery, $options: "i" },
+ })
+ .sort({ uploadedAt: -1 })
+ .limit(SEARCH_LIMIT)
+ .project({
+ _id: 1,
+ originalFileName: 1,
+ fileType: 1,
+ fileSize: 1,
+ uploadedAt: 1,
+ })
+ .toArray();
+
+ return createSuccessResponse({ files });
+ } catch (error) {
+ console.error("Search error:", error);
+ return createErrorResponse(ERROR_MESSAGES.INTERNAL_ERROR, 500);
+ }
+}
diff --git a/components/files/FileDisplay.tsx b/components/files/FileDisplay.tsx
index ad0768f..79116a5 100644
--- a/components/files/FileDisplay.tsx
+++ b/components/files/FileDisplay.tsx
@@ -2,7 +2,6 @@
import { useState } from "react";
import { TooltipProvider } from "@/components/ui/tooltip";
-import { ViewFileModal } from "@/components/modals/ViewFileModal";
import { FileEmptyState } from "@/components/files/FileEmptyState";
import { FileListView } from "@/components/files/FileListView";
import { FileGridView } from "@/components/files/FileGridView";
@@ -16,6 +15,7 @@ interface FileDisplayProps {
mode: "files" | "trash";
onFileDeleted: () => void;
onFileUpdated?: () => void;
+ onViewFile: (file: FileDocument) => void;
}
export function FileDisplay({
@@ -23,10 +23,9 @@ export function FileDisplay({
mode,
onFileDeleted,
onFileUpdated,
+ onViewFile,
}: FileDisplayProps) {
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
- const [viewFile, setViewFile] = useState(null);
- const [isViewModalOpen, setIsViewModalOpen] = useState(false);
const {
deletingFiles,
@@ -53,8 +52,7 @@ export function FileDisplay({
const handleView = (fileId: string) => {
const file = files.find(f => f._id!.toString() === fileId);
if (file) {
- setViewFile(file);
- setIsViewModalOpen(true);
+ onViewFile(file);
}
};
@@ -126,15 +124,6 @@ export function FileDisplay({
onClose={() => setRenameDialogOpen(false)}
onConfirm={confirmRename}
/>
-
- {
- setIsViewModalOpen(false);
- setViewFile(null);
- }}
- />
>
);
}
diff --git a/components/layout/DashboardHeader.tsx b/components/layout/DashboardHeader.tsx
index b4e082b..a401eef 100644
--- a/components/layout/DashboardHeader.tsx
+++ b/components/layout/DashboardHeader.tsx
@@ -13,18 +13,34 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { TooltipProvider } from "@/components/ui/tooltip";
-import { Search, SlidersHorizontal, Menu } from "lucide-react";
+import { Search, Menu, X } from "lucide-react";
import ThemeToggleButton from "@/components/ui/theme-toggle-button";
import Image from "next/image";
+import { useSearch } from "@/hooks/search/useSearch";
+import { SearchDropdown } from "@/components/search/SearchDropdown";
+import type { FileDocument } from "@/types";
interface DashboardHeaderProps {
onUploadClick?: () => void;
onMenuClick?: () => void;
+ onFileSelect?: (file: FileDocument) => void;
}
-export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) {
+export function DashboardHeader({
+ onMenuClick,
+ onFileSelect,
+}: DashboardHeaderProps) {
const { data: session } = useSession();
- const [searchQuery, setSearchQuery] = useState("");
+ const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
+ const {
+ searchQuery,
+ setSearchQuery,
+ searchResults,
+ isSearching,
+ isSearchOpen,
+ setIsSearchOpen,
+ clearSearch,
+ } = useSearch();
const handleSignOut = () => {
signOut({ callbackUrl: "/" });
@@ -42,11 +58,28 @@ export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) {
.substring(0, 2);
};
+ const handleFileSelect = (file: FileDocument) => {
+ onFileSelect?.(file);
+ clearSearch();
+ setIsMobileSearchOpen(false);
+ };
+
+ const handleSearchClose = () => {
+ setIsSearchOpen(false);
+ };
+
+ const handleMobileSearchToggle = () => {
+ if (isMobileSearchOpen) {
+ clearSearch();
+ }
+ setIsMobileSearchOpen(!isMobileSearchOpen);
+ };
+
return (
-
-
-
-
+
+ {isMobileSearchOpen ? (
+
+ ) : (
+
+ )}
@@ -147,6 +205,45 @@ export function DashboardHeader({ onMenuClick }: DashboardHeaderProps) {
+
+ {/* Mobile Search Bar */}
+ {isMobileSearchOpen && (
+
+
+
+ setSearchQuery(e.target.value)}
+ onFocus={() => {
+ if (searchQuery.trim().length >= 2) {
+ setIsSearchOpen(true);
+ }
+ }}
+ autoFocus
+ className="pl-9 pr-9 py-2 w-full bg-muted border-0 rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm"
+ />
+ {searchQuery && (
+
+
+
+ )}
+
+
+
+ )}
);
diff --git a/components/layout/DashboardLayout.tsx b/components/layout/DashboardLayout.tsx
index f463ab7..8abb2bc 100644
--- a/components/layout/DashboardLayout.tsx
+++ b/components/layout/DashboardLayout.tsx
@@ -5,6 +5,7 @@ import { DashboardHeader } from "@/components/layout/DashboardHeader";
import { Sidebar } from "@/components/layout/SidebarComponent";
import { FileDisplay } from "@/components/files/FileDisplay";
import { UploadModal } from "@/components/modals/UploadModal";
+import { ViewFileModal } from "@/components/modals/ViewFileModal";
import { PaginationControls } from "@/components/common/PaginationControls";
import type { FileDocument } from "@/types";
@@ -47,10 +48,25 @@ export function DashboardLayout({
onGoToPage,
}: DashboardLayoutProps) {
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
+ const [viewFile, setViewFile] = useState(null);
+ const [isViewModalOpen, setIsViewModalOpen] = useState(false);
+
+ const handleViewFile = (file: FileDocument) => {
+ setViewFile(file);
+ setIsViewModalOpen(true);
+ };
+
+ const handleCloseViewModal = () => {
+ setIsViewModalOpen(false);
+ setViewFile(null);
+ };
return (
-
setIsMobileSidebarOpen(true)} />
+ setIsMobileSidebarOpen(true)}
+ onFileSelect={handleViewFile}
+ />
{/* Desktop Sidebar */}
@@ -93,6 +109,7 @@ export function DashboardLayout({
files={files}
onFileDeleted={onFileAction}
onFileUpdated={onFileAction}
+ onViewFile={handleViewFile}
/>
{pagination && onNextPage && onPreviousPage && onGoToPage && (
+
+
);
}
diff --git a/components/search/SearchDropdown.tsx b/components/search/SearchDropdown.tsx
new file mode 100644
index 0000000..651fc36
--- /dev/null
+++ b/components/search/SearchDropdown.tsx
@@ -0,0 +1,128 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { Loader2, SearchX } from "lucide-react";
+import { formatBytes } from "@/lib/utils/utils";
+import { getFileIcon, getFileTypeColor } from "@/lib/utils/file-utils";
+import type { FileDocument } from "@/types";
+
+interface SearchDropdownProps {
+ isOpen: boolean;
+ isSearching: boolean;
+ searchResults: FileDocument[];
+ searchQuery: string;
+ onClose: () => void;
+ onFileSelect: (file: FileDocument) => void;
+}
+
+export function SearchDropdown({
+ isOpen,
+ isSearching,
+ searchResults,
+ searchQuery,
+ onClose,
+ onFileSelect,
+}: SearchDropdownProps) {
+ const dropdownRef = useRef(null);
+
+ // Handle click outside
+ useEffect(() => {
+ function handleClickOutside(event: MouseEvent) {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ onClose();
+ }
+ }
+
+ if (isOpen) {
+ document.addEventListener("mousedown", handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [isOpen, onClose]);
+
+ // Handle escape key
+ useEffect(() => {
+ function handleEscape(event: KeyboardEvent) {
+ if (event.key === "Escape") {
+ onClose();
+ }
+ }
+
+ if (isOpen) {
+ document.addEventListener("keydown", handleEscape);
+ }
+
+ return () => {
+ document.removeEventListener("keydown", handleEscape);
+ };
+ }, [isOpen, onClose]);
+
+ if (!isOpen || searchQuery.trim().length < 2) {
+ return null;
+ }
+
+ const handleFileClick = (file: FileDocument) => {
+ onFileSelect(file);
+ onClose();
+ };
+
+ return (
+
+ {isSearching ? (
+
+
+
+ Searching...
+
+
+ ) : searchResults.length === 0 ? (
+
+
+
+ No files match "{searchQuery}"
+
+
+ ) : (
+
+ {searchResults.map(file => {
+ const Icon = getFileIcon(file.fileType);
+ const colorClass = getFileTypeColor(file.fileType);
+
+ return (
+
+ handleFileClick(file)}
+ className="w-full flex items-center gap-3 px-4 py-3 hover:bg-muted/50 transition-colors text-left"
+ >
+
+
+
+
+
+ {file.originalFileName}
+
+
+ {file.fileType.split("/")[1]?.toUpperCase()} •{" "}
+ {formatBytes(file.fileSize)}
+
+
+
+
+ );
+ })}
+
+ )}
+
+ );
+}
diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx
index f61295c..e788b08 100644
--- a/components/ui/badge.tsx
+++ b/components/ui/badge.tsx
@@ -24,7 +24,8 @@ const badgeVariants = cva(
);
export interface BadgeProps
- extends React.HTMLAttributes,
+ extends
+ React.HTMLAttributes,
VariantProps {}
function Badge({ className, variant, ...props }: BadgeProps) {
diff --git a/hooks/search/useSearch.ts b/hooks/search/useSearch.ts
new file mode 100644
index 0000000..f3e0505
--- /dev/null
+++ b/hooks/search/useSearch.ts
@@ -0,0 +1,97 @@
+import { useState, useEffect, useCallback, useRef } from "react";
+import type { FileDocument } from "@/types";
+
+interface UseSearchResult {
+ searchQuery: string;
+ setSearchQuery: (query: string) => void;
+ searchResults: FileDocument[];
+ isSearching: boolean;
+ isSearchOpen: boolean;
+ setIsSearchOpen: (open: boolean) => void;
+ clearSearch: () => void;
+}
+
+const DEBOUNCE_DELAY = 300;
+
+export function useSearch(): UseSearchResult {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [searchResults, setSearchResults] = useState([]);
+ const [isSearching, setIsSearching] = useState(false);
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
+ const abortControllerRef = useRef(null);
+
+ const performSearch = useCallback(async (query: string) => {
+ // Cancel any pending request
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort();
+ }
+
+ if (!query || query.trim().length < 2) {
+ setSearchResults([]);
+ setIsSearching(false);
+ return;
+ }
+
+ abortControllerRef.current = new AbortController();
+ setIsSearching(true);
+
+ try {
+ const response = await fetch(
+ `/api/files/search?q=${encodeURIComponent(query.trim())}`,
+ { signal: abortControllerRef.current.signal }
+ );
+
+ if (response.ok) {
+ const data = await response.json();
+ setSearchResults(data.files);
+ setIsSearchOpen(true);
+ }
+ } catch (error) {
+ // Ignore abort errors
+ if (error instanceof Error && error.name !== "AbortError") {
+ console.error("Search failed:", error);
+ setSearchResults([]);
+ }
+ } finally {
+ setIsSearching(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ performSearch(searchQuery);
+ }, DEBOUNCE_DELAY);
+
+ return () => {
+ clearTimeout(timer);
+ };
+ }, [searchQuery, performSearch]);
+
+ // Cleanup abort controller on unmount
+ useEffect(() => {
+ return () => {
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort();
+ }
+ };
+ }, []);
+
+ const clearSearch = useCallback(() => {
+ setSearchQuery("");
+ setSearchResults([]);
+ setIsSearchOpen(false);
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort();
+ }
+ }, []);
+
+ return {
+ searchQuery,
+ setSearchQuery,
+ searchResults,
+ isSearching,
+ isSearchOpen,
+ setIsSearchOpen,
+ clearSearch,
+ };
+}
diff --git a/package-lock.json b/package-lock.json
index 3c33d12..e845f3a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,11 +20,11 @@
"csrf": "^3.1.0",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
- "next": "16.0.7",
+ "next": "16.0.10",
"next-auth": "^4.24.11",
"next-themes": "^0.4.6",
- "react": "19.1.0",
- "react-dom": "19.1.0",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"zod": "^4.1.12"
@@ -1313,9 +1313,9 @@
}
},
"node_modules/@next/env": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.7.tgz",
- "integrity": "sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
+ "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@@ -1329,9 +1329,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.7.tgz",
- "integrity": "sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz",
+ "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==",
"cpu": [
"arm64"
],
@@ -1345,9 +1345,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.7.tgz",
- "integrity": "sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz",
+ "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==",
"cpu": [
"x64"
],
@@ -1361,9 +1361,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.7.tgz",
- "integrity": "sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz",
+ "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==",
"cpu": [
"arm64"
],
@@ -1377,9 +1377,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.7.tgz",
- "integrity": "sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz",
+ "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==",
"cpu": [
"arm64"
],
@@ -1393,9 +1393,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.7.tgz",
- "integrity": "sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz",
+ "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==",
"cpu": [
"x64"
],
@@ -1409,9 +1409,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.7.tgz",
- "integrity": "sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz",
+ "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==",
"cpu": [
"x64"
],
@@ -1425,9 +1425,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.7.tgz",
- "integrity": "sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz",
+ "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==",
"cpu": [
"arm64"
],
@@ -1441,9 +1441,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.7.tgz",
- "integrity": "sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz",
+ "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==",
"cpu": [
"x64"
],
@@ -6801,12 +6801,12 @@
"license": "MIT"
},
"node_modules/next": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/next/-/next-16.0.7.tgz",
- "integrity": "sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz",
+ "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==",
"license": "MIT",
"dependencies": {
- "@next/env": "16.0.7",
+ "@next/env": "16.0.10",
"@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
@@ -6819,14 +6819,14 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "16.0.7",
- "@next/swc-darwin-x64": "16.0.7",
- "@next/swc-linux-arm64-gnu": "16.0.7",
- "@next/swc-linux-arm64-musl": "16.0.7",
- "@next/swc-linux-x64-gnu": "16.0.7",
- "@next/swc-linux-x64-musl": "16.0.7",
- "@next/swc-win32-arm64-msvc": "16.0.7",
- "@next/swc-win32-x64-msvc": "16.0.7",
+ "@next/swc-darwin-arm64": "16.0.10",
+ "@next/swc-darwin-x64": "16.0.10",
+ "@next/swc-linux-arm64-gnu": "16.0.10",
+ "@next/swc-linux-arm64-musl": "16.0.10",
+ "@next/swc-linux-x64-gnu": "16.0.10",
+ "@next/swc-linux-x64-musl": "16.0.10",
+ "@next/swc-win32-arm64-msvc": "16.0.10",
+ "@next/swc-win32-x64-msvc": "16.0.10",
"sharp": "^0.34.4"
},
"peerDependencies": {
@@ -7381,24 +7381,24 @@
}
},
"node_modules/react": {
- "version": "19.1.0",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
- "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.1.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
- "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"dependencies": {
- "scheduler": "^0.26.0"
+ "scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.1.0"
+ "react": "^19.2.3"
}
},
"node_modules/react-is": {
@@ -7659,9 +7659,9 @@
}
},
"node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/semver": {
diff --git a/package.json b/package.json
index 9b78307..c8c3346 100644
--- a/package.json
+++ b/package.json
@@ -25,11 +25,11 @@
"csrf": "^3.1.0",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
- "next": "16.0.7",
+ "next": "16.0.10",
"next-auth": "^4.24.11",
"next-themes": "^0.4.6",
- "react": "19.1.0",
- "react-dom": "19.1.0",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"zod": "^4.1.12"
@@ -42,7 +42,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "16.0.7",
+ "eslint-config-next": "16.0.10",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"tailwindcss": "^4",
diff --git a/tsconfig.json b/tsconfig.json
index d8b9323..e7ff3a2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
- "lib": ["dom", "dom.iterable", "esnext"],
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve",
+ "jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -19,9 +23,19 @@
}
],
"paths": {
- "@/*": ["./*"]
+ "@/*": [
+ "./*"
+ ]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
}
From 797871e39d7a83499fd753a2c74f7e7f0905b480 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sat, 13 Dec 2025 23:34:54 +0530
Subject: [PATCH 06/14] landing page with animations
---
app/globals.css | 121 ++++++++++
app/page.tsx | 234 ++-----------------
components/landing/Architecture.tsx | 212 +++++++++++++++++
components/landing/FeaturesGrid.tsx | 342 ++++++++++++++++++++++++++++
components/landing/Footer.tsx | 54 +++++
components/landing/Hero.tsx | 172 ++++++++++++++
components/landing/Navigation.tsx | 72 ++++++
components/landing/Pricing.tsx | 140 ++++++++++++
components/landing/TrustGrid.tsx | 48 ++++
components/landing/index.ts | 7 +
tsconfig.json | 14 +-
11 files changed, 1196 insertions(+), 220 deletions(-)
create mode 100644 components/landing/Architecture.tsx
create mode 100644 components/landing/FeaturesGrid.tsx
create mode 100644 components/landing/Footer.tsx
create mode 100644 components/landing/Hero.tsx
create mode 100644 components/landing/Navigation.tsx
create mode 100644 components/landing/Pricing.tsx
create mode 100644 components/landing/TrustGrid.tsx
create mode 100644 components/landing/index.ts
diff --git a/app/globals.css b/app/globals.css
index f1da802..3cdb8ca 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -168,3 +168,124 @@
@apply bg-background text-foreground;
}
}
+
+/* Landing Page Animations */
+@keyframes fadeUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes drawBorder {
+ from {
+ clip-path: inset(0 100% 0 0);
+ }
+ to {
+ clip-path: inset(0 0 0 0);
+ }
+}
+
+@keyframes contentFade {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(-20px);
+ }
+ to {
+ opacity: 0.5;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slowRotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.animate-fade-up {
+ animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+ opacity: 0;
+}
+
+.animate-fade-in {
+ animation: fadeIn 0.3s ease-out forwards;
+}
+
+.animate-draw-border {
+ animation: drawBorder 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+}
+
+.animate-content-fade {
+ animation: contentFade 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.4s forwards;
+ opacity: 0;
+}
+
+.animate-slide-in-right {
+ animation: slideInRight 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+}
+
+.animate-slide-in-left {
+ animation: slideInLeft 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
+}
+
+.animation-delay-100 {
+ animation-delay: 100ms;
+}
+
+.animation-delay-200 {
+ animation-delay: 200ms;
+}
+
+.animation-delay-300 {
+ animation-delay: 300ms;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .animate-fade-up,
+ .animate-fade-in,
+ .animate-draw-border,
+ .animate-content-fade,
+ .animate-slide-in-right,
+ .animate-slide-in-left {
+ animation: none;
+ opacity: 1;
+ transform: none;
+ }
+}
diff --git a/app/page.tsx b/app/page.tsx
index bd67dcf..233d49d 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -3,10 +3,16 @@
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
-import { Button } from "@/components/ui/button";
-import Image from "next/image";
import { SigninModal } from "@/components/modals/SigninModal";
-import ThemeToggleButton from "@/components/ui/theme-toggle-button";
+import {
+ Navigation,
+ Hero,
+ TrustGrid,
+ FeaturesGrid,
+ Architecture,
+ Pricing,
+ Footer,
+} from "@/components/landing";
export default function Home() {
const { data: session, status } = useSession();
@@ -14,228 +20,38 @@ export default function Home() {
const [isSigninModalOpen, setIsSigninModalOpen] = useState(false);
useEffect(() => {
- if (status === "loading") {
- return;
- }
-
- if (session) {
- router.push("/dashboard");
- }
+ if (status === "loading") return;
+ if (session) router.push("/dashboard");
}, [session, status, router]);
- const handleGetStarted = () => {
- setIsSigninModalOpen(true);
- };
+ const handleGetStarted = () => setIsSigninModalOpen(true);
if (status === "loading") {
return (
-
+
-
-
Loading...
+
+
Loading...
);
}
- if (session) {
- return null; // Will redirect to dashboard
- }
+ if (session) return null;
return (
-
- {/* Header */}
-
-
-
-
-
-
- Mini Drive
-
-
-
-
-
- Sign in
-
-
-
-
-
-
- {/* Main Content */}
-
-
-
- {/* Left Content */}
-
-
-
-
- 5GB free cloud storage
-
-
- Your files,
-
- anywhere
-
-
- Secure cloud storage with Google authentication. Access your
- files from any device, anywhere in the world.
-
-
-
-
-
- Get started free
-
-
-
- {/* Trust indicators */}
-
-
-
- ✓
-
-
No credit card required
-
-
-
- 🔒
-
-
Google secure
-
-
-
-
- {/* Right Content - Visual Element */}
-
-
- {/* Background elements */}
-
-
-
- {/* Main visual */}
-
-
- {/* App mockup */}
-
-
-
-
-
- Mini Drive
-
-
-
-
-
- {/* File list mockup */}
-
-
-
- 📄
-
-
-
- Project Notes.pdf
-
-
- 2.4 MB • Modified today
-
-
-
-
-
-
- 📷
-
-
-
- Vacation Photos
-
-
- 15 files • 245 MB
-
-
-
-
-
-
-
- +
-
-
-
- Drop files here
-
-
-
-
-
- {/* Stats */}
-
-
-
- 5GB
-
-
- Free storage
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Sign in Modal */}
+
+
+
+
+
+
+
+
setIsSigninModalOpen(false)}
/>
-
+
);
}
diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx
new file mode 100644
index 0000000..baa2e82
--- /dev/null
+++ b/components/landing/Architecture.tsx
@@ -0,0 +1,212 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { Key, Shield, Zap } from "lucide-react";
+
+export function Architecture() {
+ const [isVisible, setIsVisible] = useState(false);
+ const sectionRef = useRef
(null);
+
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ setIsVisible(true);
+ }
+ },
+ { threshold: 0.3 }
+ );
+
+ if (sectionRef.current) {
+ observer.observe(sectionRef.current);
+ }
+
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+
+
+
+
+
+ Security
+
+
+ Built on a fortress.
+
+
+ Minidrive isn't just storage; it's a vault. End-to-end
+ encryption ensures that even we can't see your data.
+
+
+
+
+
+
+
+
+
+ AES-256 Encryption
+
+
+ Military-grade encryption at rest and in transit.
+
+
+
+
+
+
+
+
+
+
+ 2-Factor Authentication
+
+
+ Secure your account with TOTP or hardware keys.
+
+
+
+
+
+
+
+
+
+
+ {"< 50ms Latency"}
+
+
+ Global edge network for instant file access.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx
new file mode 100644
index 0000000..5e1ed28
--- /dev/null
+++ b/components/landing/FeaturesGrid.tsx
@@ -0,0 +1,342 @@
+"use client";
+
+import { useRef, useState } from "react";
+import { Activity, Copy, Search, Trash2 } from "lucide-react";
+
+export function FeaturesGrid() {
+ return (
+
+
+
+
+ Core Features
+
+
+ Intelligent by design.
+
+
+
+
+
+
+ );
+}
+
+function ActivityCard() {
+ const [hovered, setHovered] = useState(false);
+ const [showTooltip, setShowTooltip] = useState(false);
+
+ return (
+ {
+ setHovered(true);
+ setTimeout(() => setShowTooltip(true), 300);
+ }}
+ onMouseLeave={() => {
+ setHovered(false);
+ setShowTooltip(false);
+ }}
+ >
+
+
+ Total visibility.
+
+
+ Track every movement. See who viewed, downloaded, or modified a file in
+ real-time.
+
+
+
+
+ {[
+ {
+ name: "Madison Scott",
+ action: "Downloaded 'Q3-Report.pdf'",
+ time: "20m ago",
+ highlight: true,
+ },
+ {
+ name: "Alex Chen",
+ action: "Viewed 'Design-System.fig'",
+ time: "1h ago",
+ highlight: false,
+ },
+ {
+ name: "Jordan Lee",
+ action: "Modified 'Brand-Guidelines'",
+ time: "3h ago",
+ highlight: false,
+ },
+ {
+ name: "Sam Parker",
+ action: "Uploaded 'Invoice_2024.pdf'",
+ time: "5h ago",
+ highlight: false,
+ },
+ ].map((item, i) => (
+
+
+
+ {item.name
+ .split(" ")
+ .map(n => n[0])
+ .join("")}
+
+
+ {item.name}
+
+
+
{item.time}
+ {item.highlight && showTooltip && (
+
+ {item.action}
+
+ — {item.time}
+
+
+ )}
+
+ ))}
+
+
+
+ );
+}
+
+function DuplicateCard() {
+ const [hovered, setHovered] = useState(false);
+ const [merged, setMerged] = useState(false);
+
+ return (
+ {
+ setHovered(true);
+ setTimeout(() => setMerged(true), 500);
+ }}
+ onMouseLeave={() => {
+ setHovered(false);
+ setMerged(false);
+ }}
+ >
+
+
+
+ Deduplication
+
+
+
+ Zero redundancy.
+
+
+ Automatic hash-matching identifies and merges duplicate files.
+
+
+
+
+
+
+ {merged && (
+
+ 1
+
+ )}
+
+
+
+ );
+}
+
+function SearchCard() {
+ const [hovered, setHovered] = useState(false);
+ const [searchText, setSearchText] = useState("");
+ const [showDropdown, setShowDropdown] = useState(false);
+ const searchRef = useRef(null);
+
+ const handleHover = () => {
+ setHovered(true);
+ let i = 0;
+ const text = "inv...";
+ searchRef.current = setInterval(() => {
+ if (i <= text.length) {
+ setSearchText(text.slice(0, i));
+ i++;
+ } else {
+ if (searchRef.current) clearInterval(searchRef.current);
+ setShowDropdown(true);
+ }
+ }, 100);
+ };
+
+ const handleLeave = () => {
+ setHovered(false);
+ if (searchRef.current) clearInterval(searchRef.current);
+ setSearchText("");
+ setShowDropdown(false);
+ };
+
+ return (
+
+
+
+
+ Search
+
+
+
+ Deep Query.
+
+
+ Search inside PDFs, Docs, and code snippets.
+
+
+
+
+
+ {searchText}
+
+ |
+
+ {showDropdown && (
+
+
+
+
+ Inv
+
+ oice_2024.pdf
+
+
+
+
+
+ Inv
+
+ estment_Plan.docx
+
+
+
+ )}
+
+
+ );
+}
+
+function TrashCard() {
+ const [hovered, setHovered] = useState(false);
+ const [filePosition, setFilePosition] = useState<
+ "active" | "trash" | "restoring"
+ >("active");
+ const [showUndo, setShowUndo] = useState(false);
+
+ const animateTrash = () => {
+ setHovered(true);
+ setTimeout(() => setFilePosition("trash"), 300);
+ setTimeout(() => setShowUndo(true), 600);
+ setTimeout(() => {
+ setFilePosition("restoring");
+ setShowUndo(false);
+ }, 1500);
+ setTimeout(() => setFilePosition("active"), 2000);
+ };
+
+ return (
+ {
+ setHovered(false);
+ setFilePosition("active");
+ setShowUndo(false);
+ }}
+ >
+
+
+
+ Recovery
+
+
+
+ Forgiving, yet secure.
+
+
+ 30-day retention for deleted items. One-click restoration.
+
+
+
+
+
+ Active
+
+
+ {(filePosition === "active" || filePosition === "restoring") && (
+
+ )}
+
+
+
+
+ Trash
+
+
+ {filePosition === "trash" && (
+
+ )}
+
+
+
+
+ {showUndo && (
+
+ File deleted.
+
+ Undo
+
+
+ )}
+
+ );
+}
diff --git a/components/landing/Footer.tsx b/components/landing/Footer.tsx
new file mode 100644
index 0000000..1163a7b
--- /dev/null
+++ b/components/landing/Footer.tsx
@@ -0,0 +1,54 @@
+import Link from "next/link";
+
+const footerLinks = {
+ Product: ["Changelog", "Features", "Security", "Pricing"],
+ Company: ["About", "Careers", "Brand", "Contact"],
+ Resources: ["Blog", "Help Center", "API Docs", "System Status"],
+ Legal: ["Privacy", "Terms", "Cookie Settings"],
+};
+
+export function Footer() {
+ return (
+
+
+
+
+
+ Minidrive
+
+
Designed for focus.
+
+
+ {Object.entries(footerLinks).map(([category, links]) => (
+
+
+ {category}
+
+
+ {links.map(link => (
+
+
+ {link}
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+ MINIDRIVE © 2025
+
+
+
+ );
+}
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
new file mode 100644
index 0000000..adde073
--- /dev/null
+++ b/components/landing/Hero.tsx
@@ -0,0 +1,172 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { ArrowRight } from "lucide-react";
+
+interface HeroProps {
+ onGetStarted: () => void;
+}
+
+export function Hero({ onGetStarted }: HeroProps) {
+ const containerRef = useRef(null);
+ const shardsRef = useRef(null);
+
+ useEffect(() => {
+ const handleMouseMove = (e: MouseEvent) => {
+ if (!shardsRef.current || !containerRef.current) return;
+ const rect = containerRef.current.getBoundingClientRect();
+ const x = (e.clientX - rect.left - rect.width / 2) / 50;
+ const y = (e.clientY - rect.top - rect.height / 2) / 50;
+
+ const shards = shardsRef.current.querySelectorAll(".shard");
+ shards.forEach((shard, i) => {
+ const depth = (i + 1) * 0.5;
+ (shard as HTMLElement).style.transform =
+ `translate(${x * depth}px, ${y * depth}px)`;
+ });
+ };
+
+ const container = containerRef.current;
+ container?.addEventListener("mousemove", handleMouseMove);
+ return () => container?.removeEventListener("mousemove", handleMouseMove);
+ }, []);
+
+ return (
+
+
+
+
+ Version 2.0 is live — Intelligent Storage
+
+
+
+
+ File management, perfected.
+
+
+
+ Minidrive is the high-performance workspace for your life's data.
+ 5GB free. Zero-knowledge encryption. Native speed.
+
+
+
+
+
+
+
+
+
+
+ {[
+ {
+ name: "Q3-Report.pdf",
+ size: "2.4 MB",
+ modified: "2 hours ago",
+ },
+ {
+ name: "Design-System.fig",
+ size: "18.2 MB",
+ modified: "Yesterday",
+ },
+ {
+ name: "Invoice_2024.pdf",
+ size: "340 KB",
+ modified: "3 days ago",
+ },
+ {
+ name: "Brand-Guidelines.pdf",
+ size: "5.1 MB",
+ modified: "Last week",
+ },
+ ].map((file, i) => (
+
+
+
+
+ {file.size}
+
+
+ {file.modified}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Duplicate Detected
+
+
+
+ Ignore
+
+
+ Merge
+
+
+
+
+
+
+ );
+}
diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx
new file mode 100644
index 0000000..5f72434
--- /dev/null
+++ b/components/landing/Navigation.tsx
@@ -0,0 +1,72 @@
+"use client";
+
+import Link from "next/link";
+import { useState, useEffect } from "react";
+
+interface NavigationProps {
+ onSignIn: () => void;
+ onGetStarted: () => void;
+}
+
+export function Navigation({ onSignIn, onGetStarted }: NavigationProps) {
+ const [scrolled, setScrolled] = useState(false);
+
+ useEffect(() => {
+ const handleScroll = () => setScrolled(window.scrollY > 10);
+ window.addEventListener("scroll", handleScroll);
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
+
+ return (
+
+
+
+ Minidrive
+
+
+
+
+ Features
+
+
+ Security
+
+
+ Pricing
+
+
+
+
+
+ Sign in
+
+
+ Get Started
+
+
+
+
+ );
+}
diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx
new file mode 100644
index 0000000..efd81e7
--- /dev/null
+++ b/components/landing/Pricing.tsx
@@ -0,0 +1,140 @@
+"use client";
+
+import { useState } from "react";
+import { Check } from "lucide-react";
+
+interface PricingProps {
+ onGetStarted: () => void;
+}
+
+export function Pricing({ onGetStarted }: PricingProps) {
+ const [annual, setAnnual] = useState(false);
+
+ const plans = [
+ {
+ name: "Starter",
+ price: "$0",
+ sub: "Forever.",
+ features: ["5 GB Storage", "Basic Search", "Standard Encryption"],
+ cta: "Start now",
+ highlighted: false,
+ },
+ {
+ name: "Pro",
+ price: annual ? "$10" : "$12",
+ sub: "per user / month",
+ features: [
+ "1 TB Storage",
+ "Duplicate Detection",
+ "Advanced Activity Logs",
+ "Priority Support",
+ ],
+ cta: "Go Pro",
+ highlighted: true,
+ },
+ {
+ name: "Enterprise",
+ price: "Custom",
+ sub: "Tailored for scale.",
+ features: [
+ "Unlimited Storage",
+ "SSO / SAML",
+ "Dedicated Success Manager",
+ "Custom Integrations",
+ ],
+ cta: "Contact Sales",
+ highlighted: false,
+ },
+ ];
+
+ return (
+
+
+
+
+ Pricing
+
+
+ Predictable scaling.
+
+
+
+
+
+
setAnnual(false)}
+ >
+ Monthly
+
+
setAnnual(true)}
+ >
+ Yearly (Save 20%)
+
+
+
+
+
+
+ {plans.map(plan => (
+
+
+ {plan.name}
+
+
+
+ {plan.price}
+
+ {plan.sub}
+
+
+
+ {plan.features.map(feature => (
+
+
+
+ {feature}
+
+
+ ))}
+
+
+
+ {plan.cta}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/components/landing/TrustGrid.tsx b/components/landing/TrustGrid.tsx
new file mode 100644
index 0000000..1dde381
--- /dev/null
+++ b/components/landing/TrustGrid.tsx
@@ -0,0 +1,48 @@
+"use client";
+
+import { useState } from "react";
+
+const trustItems = [
+ { name: "SOC2", label: "Compliant" },
+ { name: "AES-256", label: "Encryption" },
+ { name: "AWS", label: "Infrastructure" },
+ { name: "Stripe", label: "Payments" },
+ { name: "99.99%", label: "Uptime" },
+ { name: "GDPR", label: "Compliant" },
+];
+
+export function TrustGrid() {
+ const [hoveredIndex, setHoveredIndex] = useState(null);
+
+ return (
+
+
+ {trustItems.map((item, i) => (
+
setHoveredIndex(i)}
+ onMouseLeave={() => setHoveredIndex(null)}
+ >
+
+ {item.name}
+
+
+ {item.label}
+
+
+ ))}
+
+
+ );
+}
diff --git a/components/landing/index.ts b/components/landing/index.ts
new file mode 100644
index 0000000..a10986b
--- /dev/null
+++ b/components/landing/index.ts
@@ -0,0 +1,7 @@
+export { Navigation } from "./Navigation";
+export { Hero } from "./Hero";
+export { TrustGrid } from "./TrustGrid";
+export { FeaturesGrid } from "./FeaturesGrid";
+export { Architecture } from "./Architecture";
+export { Pricing } from "./Pricing";
+export { Footer } from "./Footer";
diff --git a/tsconfig.json b/tsconfig.json
index e7ff3a2..705f5ce 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "ES2017",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
+ "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -23,9 +19,7 @@
}
],
"paths": {
- "@/*": [
- "./*"
- ]
+ "@/*": ["./*"]
}
},
"include": [
@@ -35,7 +29,5 @@
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
- "exclude": [
- "node_modules"
- ]
+ "exclude": ["node_modules"]
}
From 0d3a2b1f1db158a7c42b0b1cab488aaa871b2413 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 00:29:11 +0530
Subject: [PATCH 07/14] style: Refactor design system
---
app/globals.css | 192 ++++++++-------
components/activity/ActivityList.tsx | 12 +-
components/landing/Architecture.tsx | 59 ++---
components/landing/FeaturesGrid.tsx | 326 +++++++++++++++----------
components/landing/Hero.tsx | 6 +-
components/landing/Navigation.tsx | 6 +-
components/landing/Pricing.tsx | 181 ++++++++------
components/landing/TrustGrid.tsx | 20 +-
components/layout/DashboardHeader.tsx | 4 +-
components/layout/SidebarComponent.tsx | 3 +-
lib/utils/file-utils.ts | 14 +-
package-lock.json | 59 ++++-
package.json | 1 +
13 files changed, 508 insertions(+), 375 deletions(-)
diff --git a/app/globals.css b/app/globals.css
index 3cdb8ca..fd50e5f 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -4,107 +4,91 @@
@custom-variant dark (&:is(.dark *));
:root {
- --background: oklch(1 0 0);
- --foreground: oklch(0 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0 0 0);
- --primary: oklch(0.6489 0.237 26.9728);
- --primary-foreground: oklch(1 0 0);
- --secondary: oklch(0.968 0.211 109.7692);
- --secondary-foreground: oklch(0 0 0);
- --muted: oklch(0.9551 0 0);
- --muted-foreground: oklch(0.3211 0 0);
- --accent: oklch(0.5635 0.2408 260.8178);
- --accent-foreground: oklch(1 0 0);
- --destructive: oklch(0 0 0);
- --destructive-foreground: oklch(1 0 0);
- --border: oklch(0 0 0);
- --input: oklch(0 0 0);
- --ring: oklch(0.6489 0.237 26.9728);
- --chart-1: oklch(0.6489 0.237 26.9728);
- --chart-2: oklch(0.968 0.211 109.7692);
- --chart-3: oklch(0.5635 0.2408 260.8178);
- --chart-4: oklch(0.7323 0.2492 142.4953);
- --chart-5: oklch(0.5931 0.2726 328.3634);
- --sidebar: oklch(0.9551 0 0);
- --sidebar-foreground: oklch(0 0 0);
- --sidebar-primary: oklch(0.6489 0.237 26.9728);
- --sidebar-primary-foreground: oklch(1 0 0);
- --sidebar-accent: oklch(0.5635 0.2408 260.8178);
- --sidebar-accent-foreground: oklch(1 0 0);
- --sidebar-border: oklch(0 0 0);
- --sidebar-ring: oklch(0.6489 0.237 26.9728);
- --font-sans: Roboto, ui-sans-serif, sans-serif, system-ui;
- --font-serif: Roboto Slab, ui-serif, serif;
- --font-mono: Roboto Mono, ui-monospace, monospace;
+ /* Monochromatic color scheme - Light mode */
+ --background: #ffffff;
+ --foreground: #18181b;
+ --card: #ffffff;
+ --card-foreground: #18181b;
+ --popover: #ffffff;
+ --popover-foreground: #18181b;
+ --primary: #18181b;
+ --primary-foreground: #fafafa;
+ --secondary: #f4f4f5;
+ --secondary-foreground: #18181b;
+ --muted: #f4f4f5;
+ --muted-foreground: #71717a;
+ --accent: #e4e4e7;
+ --accent-foreground: #18181b;
+ --destructive: #18181b;
+ --destructive-foreground: #fafafa;
+ --border: #d4d4d8;
+ --input: #d4d4d8;
+ --ring: #a1a1aa;
+ --chart-1: #18181b;
+ --chart-2: #52525b;
+ --chart-3: #71717a;
+ --chart-4: #a1a1aa;
+ --chart-5: #d4d4d8;
+ --sidebar: #fafafa;
+ --sidebar-foreground: #18181b;
+ --sidebar-primary: #18181b;
+ --sidebar-primary-foreground: #fafafa;
+ --sidebar-accent: #f4f4f5;
+ --sidebar-accent-foreground: #18181b;
+ --sidebar-border: #d4d4d8;
+ --sidebar-ring: #a1a1aa;
+ --font-sans: "Inter", ui-sans-serif, sans-serif, system-ui;
+ --font-serif: "Inter", ui-serif, serif;
+ --font-mono: "Geist Mono", ui-monospace, monospace;
--radius: 0px;
- --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5);
- --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5);
- --shadow-sm:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1);
- --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1);
- --shadow-md:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 2px 4px -1px hsl(0 0% 0% / 1);
+ --shadow-2xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 4px 6px -1px hsl(0 0% 0% / 1);
+ 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 8px 10px -1px hsl(0 0% 0% / 1);
- --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.5);
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
+ --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tracking-normal: 0em;
--spacing: 0.25rem;
}
.dark {
- --background: oklch(0 0 0);
- --foreground: oklch(1 0 0);
- --card: oklch(0 0 0);
- --card-foreground: oklch(1 0 0);
- --popover: oklch(0.3211 0 0);
- --popover-foreground: oklch(1 0 0);
- --primary: oklch(0.7044 0.1872 23.1858);
- --primary-foreground: oklch(0 0 0);
- --secondary: oklch(0.9691 0.2005 109.6228);
- --secondary-foreground: oklch(0 0 0);
- --muted: oklch(0.3211 0 0);
- --muted-foreground: oklch(0.8452 0 0);
- --accent: oklch(0.6755 0.1765 252.2592);
- --accent-foreground: oklch(0 0 0);
- --destructive: oklch(1 0 0);
- --destructive-foreground: oklch(0 0 0);
- --border: oklch(1 0 0);
- --input: oklch(1 0 0);
- --ring: oklch(0.7044 0.1872 23.1858);
- --chart-1: oklch(0.7044 0.1872 23.1858);
- --chart-2: oklch(0.9691 0.2005 109.6228);
- --chart-3: oklch(0.6755 0.1765 252.2592);
- --chart-4: oklch(0.7395 0.2268 142.8504);
- --chart-5: oklch(0.6131 0.2458 328.0714);
- --sidebar: oklch(0 0 0);
- --sidebar-foreground: oklch(1 0 0);
- --sidebar-primary: oklch(0.7044 0.1872 23.1858);
- --sidebar-primary-foreground: oklch(0 0 0);
- --sidebar-accent: oklch(0.6755 0.1765 252.2592);
- --sidebar-accent-foreground: oklch(0 0 0);
- --sidebar-border: oklch(1 0 0);
- --sidebar-ring: oklch(0.7044 0.1872 23.1858);
- --font-sans: Roboto, ui-sans-serif, sans-serif, system-ui;
- --font-serif: Roboto Slab, ui-serif, serif;
- --font-mono: Roboto Mono, ui-monospace, monospace;
- --radius: 0px;
- --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5);
- --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.5);
- --shadow-sm:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1);
- --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 1px 2px -1px hsl(0 0% 0% / 1);
- --shadow-md:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 2px 4px -1px hsl(0 0% 0% / 1);
- --shadow-lg:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 4px 6px -1px hsl(0 0% 0% / 1);
- --shadow-xl:
- 4px 4px 0px 0px hsl(0 0% 0% / 1), 4px 8px 10px -1px hsl(0 0% 0% / 1);
- --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.5);
+ /* Monochromatic color scheme - Dark mode */
+ --background: #09090b;
+ --foreground: #fafafa;
+ --card: #09090b;
+ --card-foreground: #fafafa;
+ --popover: #18181b;
+ --popover-foreground: #fafafa;
+ --primary: #fafafa;
+ --primary-foreground: #18181b;
+ --secondary: #27272a;
+ --secondary-foreground: #fafafa;
+ --muted: #27272a;
+ --muted-foreground: #a1a1aa;
+ --accent: #27272a;
+ --accent-foreground: #fafafa;
+ --destructive: #fafafa;
+ --destructive-foreground: #18181b;
+ --border: #3f3f46;
+ --input: #3f3f46;
+ --ring: #52525b;
+ --chart-1: #fafafa;
+ --chart-2: #d4d4d8;
+ --chart-3: #a1a1aa;
+ --chart-4: #71717a;
+ --chart-5: #52525b;
+ --sidebar: #09090b;
+ --sidebar-foreground: #fafafa;
+ --sidebar-primary: #fafafa;
+ --sidebar-primary-foreground: #18181b;
+ --sidebar-accent: #27272a;
+ --sidebar-accent-foreground: #fafafa;
+ --sidebar-border: #3f3f46;
+ --sidebar-ring: #52525b;
}
@theme inline {
@@ -239,6 +223,18 @@
}
}
+@keyframes pulseZoom {
+ 0%,
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ 50% {
+ transform: scale(1.15);
+ opacity: 0.85;
+ }
+}
+
.animate-fade-up {
animation: fadeUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
opacity: 0;
@@ -289,3 +285,13 @@
transform: none;
}
}
+
+/* Architecture section dot pattern */
+.architecture-dots {
+ background-image: radial-gradient(circle, #a1a1aa 1px, transparent 1px);
+ background-size: 20px 20px;
+}
+
+.dark .architecture-dots {
+ background-image: radial-gradient(circle, #3f3f46 1px, transparent 1px);
+}
diff --git a/components/activity/ActivityList.tsx b/components/activity/ActivityList.tsx
index 9ff6ae4..218d024 100644
--- a/components/activity/ActivityList.tsx
+++ b/components/activity/ActivityList.tsx
@@ -35,17 +35,17 @@ function getActivityIcon(action: ActivityAction) {
function getActivityColor(action: ActivityAction) {
switch (action) {
case "upload":
- return "bg-green-500/10 text-green-600 dark:text-green-400";
+ return "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300";
case "view":
- return "bg-blue-500/10 text-blue-600 dark:text-blue-400";
+ return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400";
case "download":
- return "bg-purple-500/10 text-purple-600 dark:text-purple-400";
+ return "bg-zinc-200 text-zinc-700 dark:bg-zinc-700 dark:text-zinc-300";
case "rename":
- return "bg-yellow-500/10 text-yellow-600 dark:text-yellow-400";
+ return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400";
case "delete":
- return "bg-red-500/10 text-red-600 dark:text-red-400";
+ return "bg-zinc-200 text-zinc-800 dark:bg-zinc-700 dark:text-zinc-200";
case "restore":
- return "bg-cyan-500/10 text-cyan-600 dark:text-cyan-400";
+ return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400";
}
}
diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx
index baa2e82..29c2d61 100644
--- a/components/landing/Architecture.tsx
+++ b/components/landing/Architecture.tsx
@@ -28,11 +28,7 @@ export function Architecture() {
@@ -103,57 +99,53 @@ export function Architecture() {
-
+
+ {/* Lock body */}
+ {/* Lock shackle */}
+ {/* Keyhole circle */}
+ {/* Keyhole line */}
+ {/* Grid lines - horizontal */}
@@ -162,7 +154,7 @@ export function Architecture() {
y1="130"
x2="150"
y2="130"
- stroke="#A1A1AA"
+ className="stroke-zinc-300 dark:stroke-zinc-600"
strokeWidth="0.5"
strokeDasharray="4 4"
/>
@@ -171,16 +163,17 @@ export function Architecture() {
y1="150"
x2="150"
y2="150"
- stroke="#A1A1AA"
+ className="stroke-zinc-300 dark:stroke-zinc-600"
strokeWidth="0.5"
strokeDasharray="4 4"
/>
+ {/* Grid lines - vertical */}
@@ -189,7 +182,7 @@ export function Architecture() {
y1="90"
x2="100"
y2="170"
- stroke="#A1A1AA"
+ className="stroke-zinc-300 dark:stroke-zinc-600"
strokeWidth="0.5"
strokeDasharray="4 4"
/>
@@ -198,7 +191,7 @@ export function Architecture() {
y1="90"
x2="125"
y2="170"
- stroke="#A1A1AA"
+ className="stroke-zinc-300 dark:stroke-zinc-600"
strokeWidth="0.5"
strokeDasharray="4 4"
/>
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx
index 5e1ed28..ca74b82 100644
--- a/components/landing/FeaturesGrid.tsx
+++ b/components/landing/FeaturesGrid.tsx
@@ -1,7 +1,8 @@
"use client";
-import { useRef, useState } from "react";
-import { Activity, Copy, Search, Trash2 } from "lucide-react";
+import { useEffect, useRef, useState } from "react";
+import { motion } from "framer-motion";
+import { Activity, Copy, File, RotateCcw, Search, Trash2 } from "lucide-react";
export function FeaturesGrid() {
return (
@@ -19,7 +20,7 @@ export function FeaturesGrid() {
-
+
@@ -31,22 +32,45 @@ export function FeaturesGrid() {
}
function ActivityCard() {
- const [hovered, setHovered] = useState(false);
+ const [isActive, setIsActive] = useState(false);
const [showTooltip, setShowTooltip] = useState(false);
+ const cardRef = useRef
(null);
+
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ setIsActive(true);
+ }
+ },
+ { threshold: 0.5 }
+ );
+
+ if (cardRef.current) observer.observe(cardRef.current);
+ return () => observer.disconnect();
+ }, []);
+
+ useEffect(() => {
+ if (!isActive) return;
+
+ const showTimer = setTimeout(() => setShowTooltip(true), 800);
+ const hideTimer = setTimeout(() => setShowTooltip(false), 3000);
+ const loopTimer = setInterval(() => {
+ setShowTooltip(true);
+ setTimeout(() => setShowTooltip(false), 2200);
+ }, 4000);
+
+ return () => {
+ clearTimeout(showTimer);
+ clearTimeout(hideTimer);
+ clearInterval(loopTimer);
+ };
+ }, [isActive]);
return (
{
- setHovered(true);
- setTimeout(() => setShowTooltip(true), 300);
- }}
- onMouseLeave={() => {
- setHovered(false);
- setShowTooltip(false);
- }}
+ ref={cardRef}
+ className="relative bg-white dark:bg-zinc-950 p-8 lg:col-span-2 lg:p-10"
>
@@ -92,8 +116,8 @@ function ActivityCard() {
].map((item, i) => (
@@ -125,20 +149,33 @@ function ActivityCard() {
}
function DuplicateCard() {
- const [hovered, setHovered] = useState(false);
const [merged, setMerged] = useState(false);
+ const cardRef = useRef
(null);
+
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ const animate = () => {
+ setTimeout(() => setMerged(true), 500);
+ setTimeout(() => setMerged(false), 3000);
+ };
+ animate();
+ const interval = setInterval(animate, 4000);
+ return () => clearInterval(interval);
+ }
+ },
+ { threshold: 0.5 }
+ );
+
+ if (cardRef.current) observer.observe(cardRef.current);
+ return () => observer.disconnect();
+ }, []);
return (
{
- setHovered(true);
- setTimeout(() => setMerged(true), 500);
- }}
- onMouseLeave={() => {
- setHovered(false);
- setMerged(false);
- }}
+ ref={cardRef}
+ className="relative bg-white dark:bg-zinc-950 p-8 lg:p-10"
>
@@ -175,38 +212,55 @@ function DuplicateCard() {
}
function SearchCard() {
- const [hovered, setHovered] = useState(false);
const [searchText, setSearchText] = useState("");
const [showDropdown, setShowDropdown] = useState(false);
- const searchRef = useRef
(null);
+ const cardRef = useRef(null);
+ const intervalRef = useRef(null);
- const handleHover = () => {
- setHovered(true);
- let i = 0;
- const text = "inv...";
- searchRef.current = setInterval(() => {
- if (i <= text.length) {
- setSearchText(text.slice(0, i));
- i++;
- } else {
- if (searchRef.current) clearInterval(searchRef.current);
- setShowDropdown(true);
- }
- }, 100);
- };
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ const animate = () => {
+ let i = 0;
+ const text = "inv...";
+ setSearchText("");
+ setShowDropdown(false);
- const handleLeave = () => {
- setHovered(false);
- if (searchRef.current) clearInterval(searchRef.current);
- setSearchText("");
- setShowDropdown(false);
- };
+ intervalRef.current = setInterval(() => {
+ if (i <= text.length) {
+ setSearchText(text.slice(0, i));
+ i++;
+ } else {
+ if (intervalRef.current) clearInterval(intervalRef.current);
+ setShowDropdown(true);
+ setTimeout(() => {
+ setShowDropdown(false);
+ setSearchText("");
+ }, 2000);
+ }
+ }, 100);
+ };
+
+ animate();
+ const loopInterval = setInterval(animate, 4000);
+ return () => {
+ clearInterval(loopInterval);
+ if (intervalRef.current) clearInterval(intervalRef.current);
+ };
+ }
+ },
+ { threshold: 0.5 }
+ );
+
+ if (cardRef.current) observer.observe(cardRef.current);
+ return () => observer.disconnect();
+ }, []);
return (
@@ -226,63 +280,49 @@ function SearchCard() {
{searchText}
- |
+
+ |
+
- {showDropdown && (
-
-
-
-
- Inv
-
- oice_2024.pdf
-
-
-
-
-
- Inv
-
- estment_Plan.docx
-
-
+
+
+
+
+ Inv
+
+ oice_2024.pdf
+
+
+
+
+
+ Inv
+
+ estment_Plan.docx
+
- )}
+
);
}
function TrashCard() {
- const [hovered, setHovered] = useState(false);
- const [filePosition, setFilePosition] = useState<
- "active" | "trash" | "restoring"
- >("active");
- const [showUndo, setShowUndo] = useState(false);
-
- const animateTrash = () => {
- setHovered(true);
- setTimeout(() => setFilePosition("trash"), 300);
- setTimeout(() => setShowUndo(true), 600);
- setTimeout(() => {
- setFilePosition("restoring");
- setShowUndo(false);
- }, 1500);
- setTimeout(() => setFilePosition("active"), 2000);
- };
-
return (
- {
- setHovered(false);
- setFilePosition("active");
- setShowUndo(false);
- }}
- >
+
@@ -296,47 +336,67 @@ function TrashCard() {
30-day retention for deleted items. One-click restoration.
-
-
-
+
+ {/* Active Panel */}
+
+
Active
-
- {(filePosition === "active" || filePosition === "restoring") && (
-
- )}
-
+
+
+
+
-
-
+
+ {/* Trash Panel */}
+
+
Trash
-
- {filePosition === "trash" && (
-
- )}
-
+
+
+
+
-
- {showUndo && (
-
- File deleted.
-
- Undo
+ {/* Undo Toast */}
+
+ Item deleted.
+
+ Undo
-
- )}
+
+
);
}
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
index adde073..4f2909f 100644
--- a/components/landing/Hero.tsx
+++ b/components/landing/Hero.tsx
@@ -43,11 +43,11 @@ export function Hero({ onGetStarted }: HeroProps) {
-
+
File management, perfected.
-
+
Minidrive is the high-performance workspace for your life's data.
5GB free. Zero-knowledge encryption. Native speed.
@@ -61,7 +61,7 @@ export function Hero({ onGetStarted }: HeroProps) {
View the architecture
diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx
index 5f72434..72e6198 100644
--- a/components/landing/Navigation.tsx
+++ b/components/landing/Navigation.tsx
@@ -2,6 +2,7 @@
import Link from "next/link";
import { useState, useEffect } from "react";
+import ThemeToggleButton from "@/components/ui/theme-toggle-button";
interface NavigationProps {
onSignIn: () => void;
@@ -52,10 +53,11 @@ export function Navigation({ onSignIn, onGetStarted }: NavigationProps) {
-
+
+
Sign in
diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx
index efd81e7..020ae53 100644
--- a/components/landing/Pricing.tsx
+++ b/components/landing/Pricing.tsx
@@ -7,45 +7,59 @@ interface PricingProps {
onGetStarted: () => void;
}
+const plans = [
+ {
+ name: "Starter",
+ price: "$0",
+ period: "Forever.",
+ features: [
+ "5 GB Storage",
+ "Basic Search",
+ "Standard Encryption",
+ "Email Support",
+ ],
+ cta: "Start now",
+ highlighted: false,
+ },
+ {
+ name: "Pro",
+ price: "$12",
+ period: "per user / month",
+ features: [
+ "1 TB Storage",
+ "Duplicate Detection",
+ "Advanced Activity Logs",
+ "Priority Support",
+ "API Access",
+ ],
+ cta: "Go Pro",
+ highlighted: true,
+ },
+ {
+ name: "Enterprise",
+ price: "Custom",
+ period: "Contact us",
+ features: [
+ "Unlimited Storage",
+ "SSO / SAML",
+ "Dedicated Success Manager",
+ "Custom Integrations",
+ "SLA Guarantee",
+ ],
+ cta: "Contact Sales",
+ highlighted: false,
+ },
+];
+
export function Pricing({ onGetStarted }: PricingProps) {
- const [annual, setAnnual] = useState(false);
+ const [isYearly, setIsYearly] = useState(false);
- const plans = [
- {
- name: "Starter",
- price: "$0",
- sub: "Forever.",
- features: ["5 GB Storage", "Basic Search", "Standard Encryption"],
- cta: "Start now",
- highlighted: false,
- },
- {
- name: "Pro",
- price: annual ? "$10" : "$12",
- sub: "per user / month",
- features: [
- "1 TB Storage",
- "Duplicate Detection",
- "Advanced Activity Logs",
- "Priority Support",
- ],
- cta: "Go Pro",
- highlighted: true,
- },
- {
- name: "Enterprise",
- price: "Custom",
- sub: "Tailored for scale.",
- features: [
- "Unlimited Storage",
- "SSO / SAML",
- "Dedicated Success Manager",
- "Custom Integrations",
- ],
- cta: "Contact Sales",
- highlighted: false,
- },
- ];
+ const getPrice = (price: string) => {
+ if (isYearly && price !== "Custom" && price !== "$0") {
+ return `$${Math.round(parseInt(price.slice(1)) * 0.8)}`;
+ }
+ return price;
+ };
return (
-
+ {/* Header */}
+
Pricing
Predictable scaling.
-
-
-
+ {/* Toggle */}
+
setIsYearly(false)}
+ className={`px-4 py-2 text-sm transition-colors ${
+ !isYearly
+ ? "bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900"
+ : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
}`}
- onClick={() => setAnnual(false)}
>
Monthly
setIsYearly(true)}
+ className={`px-4 py-2 text-sm transition-colors ${
+ isYearly
+ ? "bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900"
+ : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
}`}
- onClick={() => setAnnual(true)}
>
Yearly (Save 20%)
-
-
- {plans.map(plan => (
+ {/* Pricing Grid */}
+
+ {plans.map((plan, index) => (
-
- {plan.name}
-
-
-
- {plan.price}
-
-
{plan.sub}
+ {/* Plan Header */}
+
+
+ {plan.name}
+
+
+
+ {getPrice(plan.price)}
+
+
+
{plan.period}
-
+ {/* Features */}
+
{plan.features.map(feature => (
-
+
{feature}
))}
-
- {plan.cta}
-
+ {/* CTA */}
+
+
+ {plan.cta}
+
+
))}
diff --git a/components/landing/TrustGrid.tsx b/components/landing/TrustGrid.tsx
index 1dde381..6e60af6 100644
--- a/components/landing/TrustGrid.tsx
+++ b/components/landing/TrustGrid.tsx
@@ -5,7 +5,7 @@ import { useState } from "react";
const trustItems = [
{ name: "SOC2", label: "Compliant" },
{ name: "AES-256", label: "Encryption" },
- { name: "AWS", label: "Infrastructure" },
+ { name: "AZURE", label: "Infrastructure" },
{ name: "Stripe", label: "Payments" },
{ name: "99.99%", label: "Uptime" },
{ name: "GDPR", label: "Compliant" },
@@ -16,20 +16,22 @@ export function TrustGrid() {
return (
-
+
{trustItems.map((item, i) => (
= 4 ? "border-b-0 sm:border-b md:border-b-0" : ""}
+ ${i >= 3 ? "sm:border-b-0 md:border-b-0" : ""}
+ ${hoveredIndex === i ? "bg-zinc-50 dark:bg-zinc-900" : "bg-white dark:bg-zinc-950"}`}
onMouseEnter={() => setHoveredIndex(i)}
onMouseLeave={() => setHoveredIndex(null)}
>
{item.name}
-
+
{item.label}
diff --git a/components/layout/DashboardHeader.tsx b/components/layout/DashboardHeader.tsx
index a401eef..c208609 100644
--- a/components/layout/DashboardHeader.tsx
+++ b/components/layout/DashboardHeader.tsx
@@ -117,7 +117,7 @@ export function DashboardHeader({
setIsSearchOpen(true);
}
}}
- className="pl-9 md:pl-10 pr-10 md:pr-12 py-1.5 md:py-2 w-full bg-muted border-0 rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm"
+ className="pl-9 md:pl-10 pr-10 md:pr-12 py-1.5 md:py-2 w-full bg-muted border border-border rounded-full focus:bg-card focus:shadow-md focus:ring-2 focus:ring-ring transition-all text-sm"
/>
{searchQuery && (
{searchQuery && (
setBuyStorageModalOpen(true)}
>
Buy storage
diff --git a/lib/utils/file-utils.ts b/lib/utils/file-utils.ts
index 8811df0..658a662 100644
--- a/lib/utils/file-utils.ts
+++ b/lib/utils/file-utils.ts
@@ -29,24 +29,24 @@ export function getFileIcon(fileType: string): LucideIcon {
export function getFileTypeColor(fileType: string): string {
if (fileType.startsWith("image/")) {
- return "bg-green-50 text-green-700";
+ return "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300";
}
if (fileType.startsWith("video/")) {
- return "bg-purple-50 text-purple-700";
+ return "bg-zinc-200 text-zinc-800 dark:bg-zinc-700 dark:text-zinc-200";
}
if (fileType.startsWith("audio/")) {
- return "bg-blue-50 text-blue-700";
+ return "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400";
}
if (fileType.includes("pdf")) {
- return "bg-red-50 text-red-700";
+ return "bg-zinc-200 text-zinc-700 dark:bg-zinc-700 dark:text-zinc-300";
}
if (fileType.includes("text")) {
- return "bg-muted text-muted-foreground";
+ return "bg-zinc-50 text-zinc-600 dark:bg-zinc-900 dark:text-zinc-400";
}
if (fileType.includes("zip") || fileType.includes("rar")) {
- return "bg-orange-50 text-orange-700";
+ return "bg-zinc-300 text-zinc-800 dark:bg-zinc-600 dark:text-zinc-200";
}
- return "bg-muted text-muted-foreground";
+ return "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400";
}
export function getFileExtension(fileName: string): string {
diff --git a/package-lock.json b/package-lock.json
index e845f3a..bc9d077 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"csrf": "^3.1.0",
+ "framer-motion": "^12.23.26",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
"next": "16.0.10",
@@ -37,7 +38,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
- "eslint-config-next": "16.0.7",
+ "eslint-config-next": "16.0.10",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"tailwindcss": "^4",
@@ -1319,9 +1320,9 @@
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.7.tgz",
- "integrity": "sha512-hFrTNZcMEG+k7qxVxZJq3F32Kms130FAhG8lvw2zkKBgAcNOJIxlljNiCjGygvBshvaGBdf88q2CqWtnqezDHA==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.10.tgz",
+ "integrity": "sha512-b2NlWN70bbPLmfyoLvvidPKWENBYYIe017ZGUpElvQjDytCWgxPJx7L9juxHt0xHvNVA08ZHJdOyhGzon/KJuw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4776,13 +4777,13 @@
}
},
"node_modules/eslint-config-next": {
- "version": "16.0.7",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.7.tgz",
- "integrity": "sha512-WubFGLFHfk2KivkdRGfx6cGSFhaQqhERRfyO8BRx+qiGPGp7WLKcPvYC4mdx1z3VhVRcrfFzczjjTrbJZOpnEQ==",
+ "version": "16.0.10",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.10.tgz",
+ "integrity": "sha512-BxouZUm0I45K4yjOOIzj24nTi0H2cGo0y7xUmk+Po/PYtJXFBYVDS1BguE7t28efXjKdcN0tmiLivxQy//SsZg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "16.0.7",
+ "@next/eslint-plugin-next": "16.0.10",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
@@ -5323,6 +5324,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.23.26",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
+ "integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.23",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -6753,6 +6781,21 @@
"whatwg-url": "^14.1.0 || ^13.0.0"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.23.23",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
+ "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
diff --git a/package.json b/package.json
index c8c3346..172fce8 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"csrf": "^3.1.0",
+ "framer-motion": "^12.23.26",
"lucide-react": "^0.542.0",
"mongodb": "^6.19.0",
"next": "16.0.10",
From 4fa687b61e519272249860796779147674e8dc57 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 01:38:51 +0530
Subject: [PATCH 08/14] feat(ui): Add skeleton and improve UI
---
components/activity/ActivityList.tsx | 9 +-
components/common/DashboardSkeleton.tsx | 99 +++++++
components/common/LoadingScreen.tsx | 78 +++++-
components/landing/FeaturesGrid.tsx | 333 ++++++++++++++++--------
components/layout/DashboardLayout.tsx | 6 +-
components/modals/SigninModal.tsx | 57 ++--
components/ui/dialog.tsx | 38 +--
7 files changed, 460 insertions(+), 160 deletions(-)
create mode 100644 components/common/DashboardSkeleton.tsx
diff --git a/components/activity/ActivityList.tsx b/components/activity/ActivityList.tsx
index 218d024..342e70d 100644
--- a/components/activity/ActivityList.tsx
+++ b/components/activity/ActivityList.tsx
@@ -8,6 +8,7 @@ import {
Clock,
} from "lucide-react";
import { formatRelativeTime } from "@/lib/utils/utils";
+import { ActivitySkeleton } from "@/components/common/DashboardSkeleton";
import type { ActivityLog, ActivityAction } from "@/types";
interface ActivityListProps {
@@ -94,13 +95,7 @@ function getActivityText(activity: ActivityLog) {
export function ActivityList({ activities, loading }: ActivityListProps) {
if (loading) {
- return (
-
- );
+ return ;
}
if (activities.length === 0) {
diff --git a/components/common/DashboardSkeleton.tsx b/components/common/DashboardSkeleton.tsx
new file mode 100644
index 0000000..986a498
--- /dev/null
+++ b/components/common/DashboardSkeleton.tsx
@@ -0,0 +1,99 @@
+"use client";
+
+import { motion } from "framer-motion";
+
+function SkeletonPulse({ className }: { className?: string }) {
+ return (
+
+ );
+}
+
+export function DashboardSkeleton() {
+ return (
+
+ {/* Header skeleton */}
+
+
+ {/* Grid skeleton */}
+
+ {Array.from({ length: 12 }).map((_, i) => (
+
+
+
+ ))}
+
+
+ );
+}
+
+function FileCardSkeleton() {
+ return (
+
+ {/* Icon */}
+
+
+
+
+ {/* File name */}
+
+
+ {/* Badge and size row */}
+
+
+
+
+
+ {/* Date */}
+
+
+ );
+}
+
+export function ActivitySkeleton() {
+ return (
+
+
+ {Array.from({ length: 7 }).map((_, i) => (
+
+
+
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/components/common/LoadingScreen.tsx b/components/common/LoadingScreen.tsx
index 931b3cd..5dfa6f9 100644
--- a/components/common/LoadingScreen.tsx
+++ b/components/common/LoadingScreen.tsx
@@ -1,13 +1,77 @@
-interface LoadingScreenProps {
- message?: string;
+"use client";
+
+function SkeletonPulse({ className }: { className?: string }) {
+ return (
+
+ );
}
-export function LoadingScreen({ message = "Loading..." }: LoadingScreenProps) {
+export function LoadingScreen() {
return (
-
-
-
-
{message}
+
+ {/* Header skeleton */}
+
+
+
+ {/* Sidebar skeleton */}
+
+
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+ ))}
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+
+
+ {/* Main content skeleton */}
+
+
+
+
+ {Array.from({ length: 12 }).map((_, i) => (
+
+ ))}
+
+
);
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx
index ca74b82..f8228e8 100644
--- a/components/landing/FeaturesGrid.tsx
+++ b/components/landing/FeaturesGrid.tsx
@@ -2,7 +2,61 @@
import { useEffect, useRef, useState } from "react";
import { motion } from "framer-motion";
-import { Activity, Copy, File, RotateCcw, Search, Trash2 } from "lucide-react";
+import {
+ Activity,
+ Copy,
+ Eye,
+ File,
+ FileEdit,
+ RotateCcw,
+ Search,
+ Share2,
+ Trash2,
+ Upload,
+} from "lucide-react";
+
+type ActivityType = "viewed" | "renamed" | "uploaded" | "deleted" | "shared";
+
+interface ActivityItem {
+ type: ActivityType;
+ fileName: string;
+ renamedTo?: string;
+ time: string;
+}
+
+const activityData: ActivityItem[] = [
+ { type: "shared", fileName: "project_roadmap.docx", time: "5 hours ago" },
+ {
+ type: "uploaded",
+ fileName: "presentation_final.pptx",
+ time: "3 hours ago",
+ },
+ {
+ type: "renamed",
+ fileName: "IMG_20241201.png",
+ renamedTo: "team_photo.png",
+ time: "1 hour ago",
+ },
+ { type: "deleted", fileName: "old_backup_2023.zip", time: "15 min ago" },
+ { type: "viewed", fileName: "Q4_Financial_Report.pdf", time: "2 min ago" },
+];
+
+const getActivityIcon = (type: ActivityType) => {
+ switch (type) {
+ case "viewed":
+ return Eye;
+ case "renamed":
+ return FileEdit;
+ case "uploaded":
+ return Upload;
+ case "deleted":
+ return Trash2;
+ case "shared":
+ return Share2;
+ default:
+ return Eye;
+ }
+};
export function FeaturesGrid() {
return (
@@ -31,40 +85,118 @@ export function FeaturesGrid() {
);
}
+function ActivityRow({ item }: { item: ActivityItem }) {
+ const Icon = getActivityIcon(item.type);
+
+ return (
+
+
+
+
+
+
+ {item.type === "renamed" ? (
+ <>
+ Renamed {" "}
+
+ {item.fileName}
+ {" "}
+ to {" "}
+
+ {item.renamedTo}
+
+ >
+ ) : (
+ <>
+
+ {item.type}
+ {" "}
+
+ {item.fileName}
+
+ >
+ )}
+
+
+ {item.time}
+
+
+
+ );
+}
+
+interface VisibleItem extends ActivityItem {
+ id: number;
+}
+
function ActivityCard() {
const [isActive, setIsActive] = useState(false);
- const [showTooltip, setShowTooltip] = useState(false);
+ const [visibleItems, setVisibleItems] = useState
([]);
const cardRef = useRef(null);
+ const idCounterRef = useRef(0);
+ const ROW_HEIGHT = 52;
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
- if (entry.isIntersecting) {
+ if (entry.isIntersecting && !isActive) {
setIsActive(true);
}
},
- { threshold: 0.5 }
+ { threshold: 0.3 }
);
if (cardRef.current) observer.observe(cardRef.current);
return () => observer.disconnect();
- }, []);
+ }, [isActive]);
useEffect(() => {
if (!isActive) return;
- const showTimer = setTimeout(() => setShowTooltip(true), 800);
- const hideTimer = setTimeout(() => setShowTooltip(false), 3000);
- const loopTimer = setInterval(() => {
- setShowTooltip(true);
- setTimeout(() => setShowTooltip(false), 2200);
- }, 4000);
+ let currentIndex = 0;
+ let timeoutId: NodeJS.Timeout;
- return () => {
- clearTimeout(showTimer);
- clearTimeout(hideTimer);
- clearInterval(loopTimer);
+ const addNextItem = () => {
+ if (currentIndex < activityData.length) {
+ const baseItem = activityData[currentIndex];
+ const newItem: VisibleItem = {
+ ...baseItem,
+ id: idCounterRef.current++,
+ };
+ setVisibleItems(prev =>
+ [newItem, ...prev].slice(0, activityData.length)
+ );
+ currentIndex++;
+ timeoutId = setTimeout(addNextItem, 900);
+ } else {
+ // All items shown, wait then reset and start again
+ timeoutId = setTimeout(() => {
+ setVisibleItems([]);
+ currentIndex = 0;
+ timeoutId = setTimeout(addNextItem, 400);
+ }, 2000);
+ }
};
+
+ timeoutId = setTimeout(addNextItem, 400);
+
+ return () => clearTimeout(timeoutId);
}, [isActive]);
return (
@@ -79,70 +211,20 @@ function ActivityCard() {
- Total visibility.
+ Track everything.
- Track every movement. See who viewed, downloaded, or modified a file in
- real-time.
+ See every file action — views, uploads, renames, and deletions — all in
+ one timeline.
-
-
- {[
- {
- name: "Madison Scott",
- action: "Downloaded 'Q3-Report.pdf'",
- time: "20m ago",
- highlight: true,
- },
- {
- name: "Alex Chen",
- action: "Viewed 'Design-System.fig'",
- time: "1h ago",
- highlight: false,
- },
- {
- name: "Jordan Lee",
- action: "Modified 'Brand-Guidelines'",
- time: "3h ago",
- highlight: false,
- },
- {
- name: "Sam Parker",
- action: "Uploaded 'Invoice_2024.pdf'",
- time: "5h ago",
- highlight: false,
- },
- ].map((item, i) => (
-
-
-
- {item.name
- .split(" ")
- .map(n => n[0])
- .join("")}
-
-
- {item.name}
-
-
-
{item.time}
- {item.highlight && showTooltip && (
-
- {item.action}
-
- — {item.time}
-
-
- )}
-
- ))}
-
+
+ {visibleItems.map(item => (
+
+ ))}
);
@@ -151,25 +233,41 @@ function ActivityCard() {
function DuplicateCard() {
const [merged, setMerged] = useState(false);
const cardRef = useRef
(null);
+ const intervalRef = useRef(null);
+ const timeoutRefs = useRef([]);
useEffect(() => {
+ const clearAllTimers = () => {
+ if (intervalRef.current) clearInterval(intervalRef.current);
+ timeoutRefs.current.forEach(clearTimeout);
+ timeoutRefs.current = [];
+ };
+
+ const animate = () => {
+ const t1 = setTimeout(() => setMerged(true), 500);
+ const t2 = setTimeout(() => setMerged(false), 3000);
+ timeoutRefs.current.push(t1, t2);
+ };
+
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
- const animate = () => {
- setTimeout(() => setMerged(true), 500);
- setTimeout(() => setMerged(false), 3000);
- };
+ clearAllTimers();
animate();
- const interval = setInterval(animate, 4000);
- return () => clearInterval(interval);
+ intervalRef.current = setInterval(animate, 4000);
+ } else {
+ clearAllTimers();
+ setMerged(false);
}
},
{ threshold: 0.5 }
);
if (cardRef.current) observer.observe(cardRef.current);
- return () => observer.disconnect();
+ return () => {
+ observer.disconnect();
+ clearAllTimers();
+ };
}, []);
return (
@@ -215,46 +313,61 @@ function SearchCard() {
const [searchText, setSearchText] = useState("");
const [showDropdown, setShowDropdown] = useState(false);
const cardRef = useRef(null);
- const intervalRef = useRef(null);
+ const typingIntervalRef = useRef(null);
+ const loopIntervalRef = useRef(null);
+ const dropdownTimeoutRef = useRef(null);
useEffect(() => {
+ const clearAllTimers = () => {
+ if (typingIntervalRef.current) clearInterval(typingIntervalRef.current);
+ if (loopIntervalRef.current) clearInterval(loopIntervalRef.current);
+ if (dropdownTimeoutRef.current) clearTimeout(dropdownTimeoutRef.current);
+ };
+
+ const animate = () => {
+ let i = 0;
+ const text = "inv...";
+ setSearchText("");
+ setShowDropdown(false);
+
+ if (typingIntervalRef.current) clearInterval(typingIntervalRef.current);
+
+ typingIntervalRef.current = setInterval(() => {
+ if (i <= text.length) {
+ setSearchText(text.slice(0, i));
+ i++;
+ } else {
+ if (typingIntervalRef.current)
+ clearInterval(typingIntervalRef.current);
+ setShowDropdown(true);
+ dropdownTimeoutRef.current = setTimeout(() => {
+ setShowDropdown(false);
+ setSearchText("");
+ }, 2000);
+ }
+ }, 100);
+ };
+
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
- const animate = () => {
- let i = 0;
- const text = "inv...";
- setSearchText("");
- setShowDropdown(false);
-
- intervalRef.current = setInterval(() => {
- if (i <= text.length) {
- setSearchText(text.slice(0, i));
- i++;
- } else {
- if (intervalRef.current) clearInterval(intervalRef.current);
- setShowDropdown(true);
- setTimeout(() => {
- setShowDropdown(false);
- setSearchText("");
- }, 2000);
- }
- }, 100);
- };
-
+ clearAllTimers();
animate();
- const loopInterval = setInterval(animate, 4000);
- return () => {
- clearInterval(loopInterval);
- if (intervalRef.current) clearInterval(intervalRef.current);
- };
+ loopIntervalRef.current = setInterval(animate, 4000);
+ } else {
+ clearAllTimers();
+ setSearchText("");
+ setShowDropdown(false);
}
},
{ threshold: 0.5 }
);
if (cardRef.current) observer.observe(cardRef.current);
- return () => observer.disconnect();
+ return () => {
+ observer.disconnect();
+ clearAllTimers();
+ };
}, []);
return (
diff --git a/components/layout/DashboardLayout.tsx b/components/layout/DashboardLayout.tsx
index 8abb2bc..d4f9727 100644
--- a/components/layout/DashboardLayout.tsx
+++ b/components/layout/DashboardLayout.tsx
@@ -7,6 +7,7 @@ import { FileDisplay } from "@/components/files/FileDisplay";
import { UploadModal } from "@/components/modals/UploadModal";
import { ViewFileModal } from "@/components/modals/ViewFileModal";
import { PaginationControls } from "@/components/common/PaginationControls";
+import { DashboardSkeleton } from "@/components/common/DashboardSkeleton";
import type { FileDocument } from "@/types";
interface PaginationMetadata {
@@ -98,10 +99,7 @@ export function DashboardLayout({
{loading ? (
-
+
) : (
<>
-
+
- Welcome back
-
- Sign in with Google to access your secure cloud storage
-
+
+
+ Welcome back
+
+
+
+
+ Sign in with Google to access your secure cloud storage
+
+
-
+
-
+
-
-
-
- No credit card required • Secure with Google • Access anywhere
-
-
-
-
+
+
+ No credit card required • Secure with Google • Access anywhere
+
+
+
);
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
index f022c81..cc9d7ac 100644
--- a/components/ui/dialog.tsx
+++ b/components/ui/dialog.tsx
@@ -15,13 +15,13 @@ const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
- React.ElementRef,
+ React.ComponentRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
{
+ showCloseButton?: boolean;
+}
+
const DialogContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
+ React.ComponentRef,
+ DialogContentProps
+>(({ className, children, showCloseButton = true, ...props }, ref) => (
{children}
-
-
- Close
-
+ {showCloseButton && (
+
+
+ Close
+
+ )}
));
@@ -59,7 +67,7 @@ const DialogHeader = ({
}: React.HTMLAttributes) => (
,
+ React.ComponentRef
,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
,
+ React.ComponentRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
));
From 972f787e6d4572716a7233f32af6be67c041e225 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 11:44:58 +0530
Subject: [PATCH 09/14] feat(landing): Enhance Hero
---
components/landing/Hero.tsx | 172 +++++++++++++++++++++++++++++-------
1 file changed, 141 insertions(+), 31 deletions(-)
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
index 4f2909f..68565e9 100644
--- a/components/landing/Hero.tsx
+++ b/components/landing/Hero.tsx
@@ -1,7 +1,10 @@
"use client";
-import { useEffect, useRef } from "react";
-import { ArrowRight } from "lucide-react";
+import { useEffect, useRef, useState } from "react";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, Lock, Check, AlertTriangle } from "lucide-react";
+
+type DuplicateStatus = "pending" | "ignored" | "merged";
interface HeroProps {
onGetStarted: () => void;
@@ -10,25 +13,52 @@ interface HeroProps {
export function Hero({ onGetStarted }: HeroProps) {
const containerRef = useRef(null);
const shardsRef = useRef(null);
+ const [duplicateStatus, setDuplicateStatus] = useState("pending");
+
+ // Auto-reset duplicate status after showing success
+ useEffect(() => {
+ if (duplicateStatus !== "pending") {
+ const timer = setTimeout(() => setDuplicateStatus("pending"), 2000);
+ return () => clearTimeout(timer);
+ }
+ }, [duplicateStatus]);
useEffect(() => {
+ let animationFrameId: number | null = null;
+
const handleMouseMove = (e: MouseEvent) => {
if (!shardsRef.current || !containerRef.current) return;
- const rect = containerRef.current.getBoundingClientRect();
- const x = (e.clientX - rect.left - rect.width / 2) / 50;
- const y = (e.clientY - rect.top - rect.height / 2) / 50;
-
- const shards = shardsRef.current.querySelectorAll(".shard");
- shards.forEach((shard, i) => {
- const depth = (i + 1) * 0.5;
- (shard as HTMLElement).style.transform =
- `translate(${x * depth}px, ${y * depth}px)`;
+
+ // Cancel any pending animation frame to avoid stacking
+ if (animationFrameId) {
+ cancelAnimationFrame(animationFrameId);
+ }
+
+ animationFrameId = requestAnimationFrame(() => {
+ if (!shardsRef.current || !containerRef.current) return;
+
+ const rect = containerRef.current.getBoundingClientRect();
+ const x = (e.clientX - rect.left - rect.width / 2) / 50;
+ const y = (e.clientY - rect.top - rect.height / 2) / 50;
+
+ const shards = shardsRef.current.querySelectorAll(".shard");
+ shards.forEach((shard, i) => {
+ const depth = (i + 1) * 0.5;
+ (shard as HTMLElement).style.transform =
+ `translate(${x * depth}px, ${y * depth}px)`;
+ });
});
};
const container = containerRef.current;
container?.addEventListener("mousemove", handleMouseMove);
- return () => container?.removeEventListener("mousemove", handleMouseMove);
+
+ return () => {
+ container?.removeEventListener("mousemove", handleMouseMove);
+ if (animationFrameId) {
+ cancelAnimationFrame(animationFrameId);
+ }
+ };
}, []);
return (
@@ -132,19 +162,54 @@ export function Hero({ onGetStarted }: HeroProps) {
-
+ {/* Uploading Box */}
+
-
-
Uploading...
+
+
+ Uploading
+
+ {[0, 1, 2].map((i) => (
+
+ .
+
+ ))}
+
+
-
-
+ {/* Encryption Box */}
+
-
+
+
+
AES-256
@@ -152,18 +217,63 @@ export function Hero({ onGetStarted }: HeroProps) {
Encrypted
-
-
- Duplicate Detected
-
-
-
- Ignore
-
-
- Merge
-
-
+ {/* Duplicate Detected Box */}
+
+
+ {duplicateStatus === "pending" ? (
+
+
+
+
+
+
+ Duplicate Detected
+
+
+
+ setDuplicateStatus("ignored")}
+ >
+ Ignore
+
+ setDuplicateStatus("merged")}
+ >
+ Merge
+
+
+
+ ) : (
+
+
+
+ {duplicateStatus === "ignored" ? "Ignored Successfully" : "Merged Successfully"}
+
+
+ )}
+
From 8015f4998c4b5a96c96f26934ecfd93d3b5d993f Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 12:12:45 +0530
Subject: [PATCH 10/14] feat(landing): Enhance FeaturesGrid
---
components/landing/FeaturesGrid.tsx | 61 +++++++++++++++++++++++++++--
components/landing/Hero.tsx | 43 +++++++++++++++-----
2 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx
index f8228e8..5cde369 100644
--- a/components/landing/FeaturesGrid.tsx
+++ b/components/landing/FeaturesGrid.tsx
@@ -8,6 +8,7 @@ import {
Eye,
File,
FileEdit,
+ MousePointer2,
RotateCcw,
Search,
Share2,
@@ -469,7 +470,9 @@ function TrashCard() {
className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 z-10 relative"
>
-
+
+ Invoice_2024.pdf
+
@@ -486,10 +489,12 @@ function TrashCard() {
repeat: Infinity,
repeatDelay: 1,
}}
- className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 opacity-50 grayscale"
+ className="mt-6 sm:mt-8 w-full p-2 border border-zinc-200 dark:border-zinc-700 flex items-center space-x-2 sm:space-x-3 bg-white dark:bg-zinc-900 opacity-50 grayscale blur-[1px]"
>
-
+
+ Invoice_2024.pdf
+
@@ -509,6 +514,56 @@ function TrashCard() {
Undo
+
+ {/* Animated Mouse Pointer */}
+
+
+
);
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
index 68565e9..ff590ea 100644
--- a/components/landing/Hero.tsx
+++ b/components/landing/Hero.tsx
@@ -13,7 +13,8 @@ interface HeroProps {
export function Hero({ onGetStarted }: HeroProps) {
const containerRef = useRef(null);
const shardsRef = useRef(null);
- const [duplicateStatus, setDuplicateStatus] = useState("pending");
+ const [duplicateStatus, setDuplicateStatus] =
+ useState("pending");
// Auto-reset duplicate status after showing success
useEffect(() => {
@@ -36,7 +37,7 @@ export function Hero({ onGetStarted }: HeroProps) {
animationFrameId = requestAnimationFrame(() => {
if (!shardsRef.current || !containerRef.current) return;
-
+
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left - rect.width / 2) / 50;
const y = (e.clientY - rect.top - rect.height / 2) / 50;
@@ -52,7 +53,7 @@ export function Hero({ onGetStarted }: HeroProps) {
const container = containerRef.current;
container?.addEventListener("mousemove", handleMouseMove);
-
+
return () => {
container?.removeEventListener("mousemove", handleMouseMove);
if (animationFrameId) {
@@ -168,12 +169,16 @@ export function Hero({ onGetStarted }: HeroProps) {
Uploading
- {[0, 1, 2].map((i) => (
+ {[0, 1, 2].map(i => (
@@ -206,7 +215,12 @@ export function Hero({ onGetStarted }: HeroProps) {
@@ -231,7 +245,11 @@ export function Hero({ onGetStarted }: HeroProps) {
@@ -242,7 +260,10 @@ export function Hero({ onGetStarted }: HeroProps) {
setDuplicateStatus("ignored")}
>
@@ -269,7 +290,9 @@ export function Hero({ onGetStarted }: HeroProps) {
>
- {duplicateStatus === "ignored" ? "Ignored Successfully" : "Merged Successfully"}
+ {duplicateStatus === "ignored"
+ ? "Ignored Successfully"
+ : "Merged Successfully"}
)}
From ea232fc8d73566530845b6d0f31110ff5576ad06 Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 12:30:39 +0530
Subject: [PATCH 11/14] Improve navigation and pricing UX
---
app/page.tsx | 4 +
components/landing/Navigation.tsx | 30 ++--
components/landing/Pricing.tsx | 226 +++++++++++-------------------
hooks/useScrollRestore.ts | 37 +++++
4 files changed, 144 insertions(+), 153 deletions(-)
create mode 100644 hooks/useScrollRestore.ts
diff --git a/app/page.tsx b/app/page.tsx
index 233d49d..69ac072 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -13,12 +13,16 @@ import {
Pricing,
Footer,
} from "@/components/landing";
+import { useScrollRestore } from "@/hooks/useScrollRestore";
export default function Home() {
const { data: session, status } = useSession();
const router = useRouter();
const [isSigninModalOpen, setIsSigninModalOpen] = useState(false);
+ // Restore scroll position on page refresh (only after loading completes)
+ useScrollRestore(status !== "loading" && !session);
+
useEffect(() => {
if (status === "loading") return;
if (session) router.push("/dashboard");
diff --git a/components/landing/Navigation.tsx b/components/landing/Navigation.tsx
index 72e6198..99e6c50 100644
--- a/components/landing/Navigation.tsx
+++ b/components/landing/Navigation.tsx
@@ -33,24 +33,36 @@ export function Navigation({ onSignIn, onGetStarted }: NavigationProps) {
-
+ document
+ .getElementById("features")
+ ?.scrollIntoView({ behavior: "smooth" })
+ }
className="text-sm text-zinc-900 dark:text-zinc-100 transition-colors hover:text-zinc-500"
>
Features
-
-
+
+ document
+ .getElementById("security")
+ ?.scrollIntoView({ behavior: "smooth" })
+ }
className="text-sm text-zinc-900 dark:text-zinc-100 transition-colors hover:text-zinc-500"
>
Security
-
-
+
+ document
+ .getElementById("pricing")
+ ?.scrollIntoView({ behavior: "smooth" })
+ }
className="text-sm text-zinc-900 dark:text-zinc-100 transition-colors hover:text-zinc-500"
>
Pricing
-
+
diff --git a/components/landing/Pricing.tsx b/components/landing/Pricing.tsx
index 020ae53..69caf04 100644
--- a/components/landing/Pricing.tsx
+++ b/components/landing/Pricing.tsx
@@ -1,163 +1,101 @@
"use client";
-import { useState } from "react";
import { Check } from "lucide-react";
interface PricingProps {
onGetStarted: () => void;
}
-const plans = [
- {
- name: "Starter",
- price: "$0",
- period: "Forever.",
- features: [
- "5 GB Storage",
- "Basic Search",
- "Standard Encryption",
- "Email Support",
- ],
- cta: "Start now",
- highlighted: false,
- },
- {
- name: "Pro",
- price: "$12",
- period: "per user / month",
- features: [
- "1 TB Storage",
- "Duplicate Detection",
- "Advanced Activity Logs",
- "Priority Support",
- "API Access",
- ],
- cta: "Go Pro",
- highlighted: true,
- },
- {
- name: "Enterprise",
- price: "Custom",
- period: "Contact us",
- features: [
- "Unlimited Storage",
- "SSO / SAML",
- "Dedicated Success Manager",
- "Custom Integrations",
- "SLA Guarantee",
- ],
- cta: "Contact Sales",
- highlighted: false,
- },
-];
-
-export function Pricing({ onGetStarted }: PricingProps) {
- const [isYearly, setIsYearly] = useState(false);
+interface PricingCardProps {
+ title: string;
+ price: string;
+ features: string[];
+ highlighted?: boolean;
+ onGetStarted: () => void;
+}
- const getPrice = (price: string) => {
- if (isYearly && price !== "Custom" && price !== "$0") {
- return `$${Math.round(parseInt(price.slice(1)) * 0.8)}`;
- }
- return price;
- };
+function PricingCard({
+ title,
+ price,
+ features,
+ highlighted,
+ onGetStarted,
+}: PricingCardProps) {
+ return (
+
+
+ {title}
+
+
+
+ {price}
+
+
+
+ {features.map((feature, i) => (
+
+
+ {feature}
+
+ ))}
+
+
+ {title === "Free" ? "Get Started" : "Subscribe"}
+
+
+ );
+}
+export function Pricing({ onGetStarted }: PricingProps) {
return (
-
- {/* Header */}
-
-
- Pricing
-
-
- Predictable scaling.
+
+
+
+ Simple pricing
-
- {/* Toggle */}
-
- setIsYearly(false)}
- className={`px-4 py-2 text-sm transition-colors ${
- !isYearly
- ? "bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900"
- : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
- }`}
- >
- Monthly
-
- setIsYearly(true)}
- className={`px-4 py-2 text-sm transition-colors ${
- isYearly
- ? "bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900"
- : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
- }`}
- >
- Yearly (Save 20%)
-
-
+
+ No hidden fees. No complicated tiers.
+
-
- {/* Pricing Grid */}
-
- {plans.map((plan, index) => (
-
- {/* Plan Header */}
-
-
- {plan.name}
-
-
-
- {getPrice(plan.price)}
-
-
-
{plan.period}
-
-
- {/* Features */}
-
- {plan.features.map(feature => (
-
-
-
- {feature}
-
-
- ))}
-
-
- {/* CTA */}
-
-
- {plan.cta}
-
-
-
- ))}
+
diff --git a/hooks/useScrollRestore.ts b/hooks/useScrollRestore.ts
new file mode 100644
index 0000000..ecd330d
--- /dev/null
+++ b/hooks/useScrollRestore.ts
@@ -0,0 +1,37 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+
+const SCROLL_KEY = "landing_scroll_position";
+
+export function useScrollRestore(isReady: boolean = true) {
+ const hasRestored = useRef(false);
+
+ useEffect(() => {
+ // Save scroll position before unload
+ const handleBeforeUnload = () => {
+ sessionStorage.setItem(SCROLL_KEY, window.scrollY.toString());
+ };
+
+ window.addEventListener("beforeunload", handleBeforeUnload);
+
+ return () => {
+ window.removeEventListener("beforeunload", handleBeforeUnload);
+ };
+ }, []);
+
+ useEffect(() => {
+ // Only restore once when content is ready
+ if (!isReady || hasRestored.current) return;
+
+ const savedPosition = sessionStorage.getItem(SCROLL_KEY);
+ if (savedPosition) {
+ const position = parseInt(savedPosition, 10);
+ // Delay to ensure DOM is fully rendered
+ setTimeout(() => {
+ window.scrollTo({ top: position, behavior: "instant" });
+ }, 100);
+ }
+ hasRestored.current = true;
+ }, [isReady]);
+}
From 028ee3f7f146e76c9a8305e33c3100b1fa78e87a Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 13:10:07 +0530
Subject: [PATCH 12/14] feat(landing): enhance landing pages
---
components/landing/Architecture.tsx | 315 ++++++++++++++--------------
components/landing/FeaturesGrid.tsx | 149 ++++++++-----
components/landing/Footer.tsx | 12 +-
components/landing/Hero.tsx | 166 ++++++++++-----
4 files changed, 376 insertions(+), 266 deletions(-)
diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx
index 29c2d61..3411be1 100644
--- a/components/landing/Architecture.tsx
+++ b/components/landing/Architecture.tsx
@@ -1,201 +1,212 @@
"use client";
-import { useEffect, useRef, useState } from "react";
import { Key, Shield, Zap } from "lucide-react";
export function Architecture() {
- const [isVisible, setIsVisible] = useState(false);
- const sectionRef = useRef(null);
-
- useEffect(() => {
- const observer = new IntersectionObserver(
- ([entry]) => {
- if (entry.isIntersecting) {
- setIsVisible(true);
- }
- },
- { threshold: 0.3 }
- );
-
- if (sectionRef.current) {
- observer.observe(sectionRef.current);
- }
-
- return () => observer.disconnect();
- }, []);
-
return (
-
-
+ {/* Dot Grid Background */}
+
+
+
+
+ {/* Left - Copy */}
-
+
Security
-
-
+
+
Built on a fortress.
-
+
Minidrive isn't just storage; it's a vault. End-to-end
encryption ensures that even we can't see your data.
-
-
-
-
-
-
-
- AES-256 Encryption
-
-
- Military-grade encryption at rest and in transit.
-
-
+ {/* Feature List */}
+
+
+
+
+ AES-256 Encryption
+
-
-
-
-
-
-
-
- 2-Factor Authentication
-
-
- Secure your account with TOTP or hardware keys.
-
-
+
+
+
+ 2-Factor Authentication
+
-
-
-
-
-
-
-
- {"< 50ms Latency"}
-
-
- Global edge network for instant file access.
-
-
+
+
+
+ < 50ms Latency
+
+ {/* Right - Wireframe Lock Visualization */}
-
+
+ {/* Wireframe Lock SVG */}
- {/* Lock body */}
+ {/* Lock Body - Outer */}
- {/* Lock shackle */}
+
+ {/* Lock Body - Inner Grid */}
+
+
+
+
+
+ {/* Lock Shackle */}
- {/* Keyhole circle */}
+
+ {/* Keyhole */}
+
+
+ {/* Corner Details */}
+
- {/* Keyhole line */}
-
- {/* Grid lines - horizontal */}
-
-
+
+
+ {/* Decorative Lines */}
- {/* Grid lines - vertical */}
+ x1="20"
+ y1="100"
+ x2="35"
+ y2="100"
+ className="text-zinc-300 dark:text-zinc-600"
+ strokeDasharray="2 2"
+ />
+
+ {/* Floating Labels */}
+
+ 256-bit
+
+
+ encrypted
+
diff --git a/components/landing/FeaturesGrid.tsx b/components/landing/FeaturesGrid.tsx
index 5cde369..d2c6230 100644
--- a/components/landing/FeaturesGrid.tsx
+++ b/components/landing/FeaturesGrid.tsx
@@ -232,50 +232,8 @@ function ActivityCard() {
}
function DuplicateCard() {
- const [merged, setMerged] = useState(false);
- const cardRef = useRef
(null);
- const intervalRef = useRef(null);
- const timeoutRefs = useRef([]);
-
- useEffect(() => {
- const clearAllTimers = () => {
- if (intervalRef.current) clearInterval(intervalRef.current);
- timeoutRefs.current.forEach(clearTimeout);
- timeoutRefs.current = [];
- };
-
- const animate = () => {
- const t1 = setTimeout(() => setMerged(true), 500);
- const t2 = setTimeout(() => setMerged(false), 3000);
- timeoutRefs.current.push(t1, t2);
- };
-
- const observer = new IntersectionObserver(
- ([entry]) => {
- if (entry.isIntersecting) {
- clearAllTimers();
- animate();
- intervalRef.current = setInterval(animate, 4000);
- } else {
- clearAllTimers();
- setMerged(false);
- }
- },
- { threshold: 0.5 }
- );
-
- if (cardRef.current) observer.observe(cardRef.current);
- return () => {
- observer.disconnect();
- clearAllTimers();
- };
- }, []);
-
return (
-
+
@@ -289,22 +247,99 @@ function DuplicateCard() {
Automatic hash-matching identifies and merges duplicate files.
-
+
+
+ {/* Primary File (The one that stays) */}
+
-
-
-
-
- {merged && (
-
- 1
-
- )}
-
+
+
+
+
+
+ {/* Duplicate File (The one that merges) */}
+
+
+
+
+
+
+ {/* Scanner Line */}
+
+
+ {/* Match Text */}
+
+ HASH MATCH
+
+
+ {/* Saved Text */}
+
+
+ DEDUPLICATED
+
+
);
diff --git a/components/landing/Footer.tsx b/components/landing/Footer.tsx
index 1163a7b..4dca7d6 100644
--- a/components/landing/Footer.tsx
+++ b/components/landing/Footer.tsx
@@ -10,7 +10,7 @@ const footerLinks = {
export function Footer() {
return (
-
+
Minidrive
-
Designed for focus.
+
+ Your files. Precisely managed.
+
{Object.entries(footerLinks).map(([category, links]) => (
@@ -44,9 +46,9 @@ export function Footer() {
-
-
- MINIDRIVE © 2025
+
+
+ MINIDRIVE
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
index ff590ea..5973921 100644
--- a/components/landing/Hero.tsx
+++ b/components/landing/Hero.tsx
@@ -2,7 +2,15 @@
import { useEffect, useRef, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
-import { ArrowRight, Lock, Check, AlertTriangle } from "lucide-react";
+import {
+ ArrowRight,
+ Lock,
+ Check,
+ AlertTriangle,
+ FileText,
+ Folder,
+ MoreHorizontal,
+} from "lucide-react";
type DuplicateStatus = "pending" | "ignored" | "merged";
@@ -101,64 +109,118 @@ export function Hero({ onGetStarted }: HeroProps) {
-
-
-
-
-
- All Files
-
+
+ {/* Mock Window Header */}
+
+
-
-
- {[
- {
- name: "Q3-Report.pdf",
- size: "2.4 MB",
- modified: "2 hours ago",
- },
- {
- name: "Design-System.fig",
- size: "18.2 MB",
- modified: "Yesterday",
- },
- {
- name: "Invoice_2024.pdf",
- size: "340 KB",
- modified: "3 days ago",
- },
- {
- name: "Brand-Guidelines.pdf",
- size: "5.1 MB",
- modified: "Last week",
- },
- ].map((file, i) => (
-
-
-
-
- {file.name}
-
+ {/* Mock Window Body */}
+
+
+
+ All Files
+
+
+
+
+ {[
+ {
+ name: "Project_Alpha_Specs.pdf",
+ size: "2.4 MB",
+ type: "pdf",
+ },
+ {
+ name: "Financials_Q3.xlsx",
+ size: "1.1 MB",
+ type: "sheet",
+ },
+ {
+ name: "Design_System_v2.fig",
+ size: "14.2 MB",
+ type: "fig",
+ },
+ {
+ name: "Architecture_Review.docx",
+ size: "890 KB",
+ type: "doc",
+ },
+ ].map((file, i) => (
+
+
+ {file.type === "pdf" ? (
+
+ ) : (
+
+ )}
+
+ {file.name}
+
+
+
+
+ {file.size}
+
+
+
-
-
- {file.size}
-
-
- {file.modified}
+ ))}
+
+ {/* Animated Upload Row */}
+
+
+
+
+ Uploading Files
+
+ {[0, 1, 2].map(i => (
+
+ .
+
+ ))}
+
-
- ))}
+
+
From 77da3d661ff8359326743e9e6bfe776c9c52d31c Mon Sep 17 00:00:00 2001
From: Abul
Date: Sun, 14 Dec 2025 13:21:37 +0530
Subject: [PATCH 13/14] feat(landing): Adjust landing
---
components/landing/Architecture.tsx | 16 ++++++++--------
components/landing/Hero.tsx | 2 +-
components/landing/TrustGrid.tsx | 10 +++++-----
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/components/landing/Architecture.tsx b/components/landing/Architecture.tsx
index 3411be1..18d7116 100644
--- a/components/landing/Architecture.tsx
+++ b/components/landing/Architecture.tsx
@@ -34,25 +34,25 @@ export function Architecture() {
{/* Feature List */}
-
-
-
- AES-256 Encryption
-
-
- 2-Factor Authentication
+ Google OAuth Authentication
+
+
+
+
+
+ CSRF Token Protection
- < 50ms Latency
+ Time-Limited SAS URLs
diff --git a/components/landing/Hero.tsx b/components/landing/Hero.tsx
index 5973921..af2b24a 100644
--- a/components/landing/Hero.tsx
+++ b/components/landing/Hero.tsx
@@ -294,7 +294,7 @@ export function Hero({ onGetStarted }: HeroProps) {
{/* Duplicate Detected Box */}
-
+
{duplicateStatus === "pending" ? (
Date: Fri, 13 Feb 2026 18:56:50 +0530
Subject: [PATCH 14/14] update readme
---
README.md | 74 +++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 53 insertions(+), 21 deletions(-)
diff --git a/README.md b/README.md
index e215bc4..4f9f2a8 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,68 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# Mini Drive
-## Getting Started
+A personal cloud storage application built with Next.js and Azure Blob Storage.
-First, run the development server:
+## Features
+
+- Google OAuth authentication
+- File upload with progress tracking (up to 5GB)
+- File management (rename, delete, star, restore)
+- Search and filtering
+- Activity tracking
+- Dark mode
+
+## Tech Stack
+
+- Next.js 16 (App Router)
+- TypeScript
+- MongoDB
+- Azure Blob Storage
+- NextAuth.js
+- Tailwind CSS + shadcn/ui
+
+## Setup
+
+1. Clone and install dependencies:
```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
+npm install
+```
+
+2. Copy `.env.example` to `.env.local` and fill in your credentials:
+
+```bash
+cp .env.example .env.local
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+3. Set up required services:
+ - Google OAuth credentials
+ - MongoDB database
+ - Azure Storage account
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+4. Run the development server:
+
+```bash
+npm run dev
+```
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+Open [http://localhost:3000](http://localhost:3000)
-## Learn More
+## Commands
-To learn more about Next.js, take a look at the following resources:
+```bash
+npm run dev # Start dev server
+npm run build # Build for production
+npm run lint # Run ESLint
+npm run type # TypeScript check
+npm run format # Format code with Prettier
+```
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+## How It Works
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+Files are uploaded directly from the browser to Azure Blob Storage using pre-signed URLs. File metadata is stored in MongoDB. This keeps the server lightweight and uploads fast.
-## Deploy on Vercel
+Authentication uses NextAuth with Google OAuth. All routes under `/dashboard` and `/api` are protected by middleware.
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+## License
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+MIT