From e2b2419f54addf6ec8c5e157943e2f4f462d06e4 Mon Sep 17 00:00:00 2001 From: WeeXnes Date: Wed, 11 Jun 2025 22:05:20 +0200 Subject: [PATCH] initial commit --- .gitignore | 45 ++ .modified | 0 README.md | 5 + apphosting.yaml | 7 + components.json | 21 + next.config.ts | 26 + package.json | 69 ++ postcss.config.mjs | 8 + src/ai/dev.ts | 1 + src/ai/genkit.ts | 7 + src/app/browse/components/file-list-item.tsx | 50 ++ src/app/browse/components/file-list.tsx | 16 + src/app/browse/page.tsx | 37 + src/app/cdn/[fileId]/route.ts | 52 ++ src/app/favicon.ico | Bin 0 -> 248763 bytes src/app/files/[fileId]/page.tsx | 106 +++ src/app/globals.css | 88 ++ src/app/layout.tsx | 32 + src/app/login/components/login-form.tsx | 77 ++ src/app/login/page.tsx | 22 + src/app/page.tsx | 15 + .../upload/components/file-upload-form.tsx | 147 ++++ src/app/upload/page.tsx | 20 + src/components/core/AppLogo.tsx | 35 + src/components/core/Header.tsx | 53 ++ src/components/icons/FileTypeIcon.tsx | 74 ++ src/components/ui/accordion.tsx | 58 ++ src/components/ui/alert-dialog.tsx | 141 ++++ src/components/ui/alert.tsx | 59 ++ src/components/ui/avatar.tsx | 50 ++ src/components/ui/badge.tsx | 36 + src/components/ui/button.tsx | 56 ++ src/components/ui/calendar.tsx | 70 ++ src/components/ui/card.tsx | 79 ++ src/components/ui/chart.tsx | 365 +++++++++ src/components/ui/checkbox.tsx | 30 + src/components/ui/dialog.tsx | 122 +++ src/components/ui/dropdown-menu.tsx | 200 +++++ src/components/ui/form.tsx | 178 ++++ src/components/ui/input.tsx | 22 + src/components/ui/label.tsx | 26 + src/components/ui/menubar.tsx | 256 ++++++ src/components/ui/popover.tsx | 31 + src/components/ui/progress.tsx | 28 + src/components/ui/radio-group.tsx | 44 + src/components/ui/scroll-area.tsx | 48 ++ src/components/ui/select.tsx | 160 ++++ src/components/ui/separator.tsx | 31 + src/components/ui/sheet.tsx | 140 ++++ src/components/ui/sidebar.tsx | 763 ++++++++++++++++++ src/components/ui/skeleton.tsx | 15 + src/components/ui/slider.tsx | 28 + src/components/ui/switch.tsx | 29 + src/components/ui/table.tsx | 117 +++ src/components/ui/tabs.tsx | 55 ++ src/components/ui/textarea.tsx | 21 + src/components/ui/toast.tsx | 129 +++ src/components/ui/toaster.tsx | 35 + src/components/ui/tooltip.tsx | 30 + src/hooks/use-mobile.tsx | 19 + src/hooks/use-toast.ts | 194 +++++ src/lib/actions/auth.actions.ts | 46 ++ src/lib/actions/file.actions.ts | 74 ++ src/lib/auth.ts | 16 + src/lib/constants.ts | 3 + src/lib/file-service.ts | 88 ++ src/lib/utils.ts | 6 + src/middleware.ts | 59 ++ src/types/index.ts | 9 + tailwind.config.ts | 99 +++ tsconfig.json | 27 + 71 files changed, 5105 insertions(+) create mode 100644 .gitignore create mode 100644 .modified create mode 100644 README.md create mode 100644 apphosting.yaml create mode 100644 components.json create mode 100644 next.config.ts create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 src/ai/dev.ts create mode 100644 src/ai/genkit.ts create mode 100644 src/app/browse/components/file-list-item.tsx create mode 100644 src/app/browse/components/file-list.tsx create mode 100644 src/app/browse/page.tsx create mode 100644 src/app/cdn/[fileId]/route.ts create mode 100644 src/app/favicon.ico create mode 100644 src/app/files/[fileId]/page.tsx create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/login/components/login-form.tsx create mode 100644 src/app/login/page.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/upload/components/file-upload-form.tsx create mode 100644 src/app/upload/page.tsx create mode 100644 src/components/core/AppLogo.tsx create mode 100644 src/components/core/Header.tsx create mode 100644 src/components/icons/FileTypeIcon.tsx create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/menubar.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toaster.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/hooks/use-mobile.tsx create mode 100644 src/hooks/use-toast.ts create mode 100644 src/lib/actions/auth.actions.ts create mode 100644 src/lib/actions/file.actions.ts create mode 100644 src/lib/auth.ts create mode 100644 src/lib/constants.ts create mode 100644 src/lib/file-service.ts create mode 100644 src/lib/utils.ts create mode 100644 src/middleware.ts create mode 100644 src/types/index.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fad0cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +.genkit/* +.env* + +# firebase +firebase-debug.log +firestore-debug.log \ No newline at end of file diff --git a/.modified b/.modified new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc730c7 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Firebase Studio + +This is a NextJS starter in Firebase Studio. + +To get started, take a look at src/app/page.tsx. diff --git a/apphosting.yaml b/apphosting.yaml new file mode 100644 index 0000000..a55af7b --- /dev/null +++ b/apphosting.yaml @@ -0,0 +1,7 @@ +# Settings to manage and configure a Firebase App Hosting backend. +# https://firebase.google.com/docs/app-hosting/configure + +runConfig: + # Increase this value if you'd like to automatically spin up + # more instances in response to increased traffic. + maxInstances: 1 diff --git a/components.json b/components.json new file mode 100644 index 0000000..d710b49 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..3f21ce3 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,26 @@ +import type {NextConfig} from 'next'; + +const nextConfig: NextConfig = { + /* config options here */ + typescript: { + ignoreBuildErrors: true, + }, + eslint: { + ignoreDuringBuilds: true, + }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'placehold.co', + port: '', + pathname: '/**', + }, + ], + }, + serverActions: { + bodySizeLimit: '500mb', + }, +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..5d88153 --- /dev/null +++ b/package.json @@ -0,0 +1,69 @@ +{ + "name": "vivid_cdn", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack -p 9002", + "genkit:dev": "genkit start -- tsx src/ai/dev.ts", + "genkit:watch": "genkit start -- tsx --watch src/ai/dev.ts", + "build": "next build", + "start": "next start", + "lint": "next lint", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@genkit-ai/googleai": "^1.8.0", + "@genkit-ai/next": "^1.8.0", + "@hookform/resolvers": "^4.1.3", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-avatar": "^1.1.3", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-menubar": "^1.1.6", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slider": "^1.2.3", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-switch": "^1.1.3", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-tooltip": "^1.1.8", + "bcryptjs": "^2.4.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "dotenv": "^16.5.0", + "firebase": "^11.8.1", + "genkit": "^1.8.0", + "lucide-react": "^0.475.0", + "next": "15.3.3", + "patch-package": "^8.0.0", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.54.2", + "recharts": "^2.15.1", + "tailwind-merge": "^3.0.1", + "tailwindcss-animate": "^1.0.7", + "uuid": "^9.0.1", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@types/uuid": "^9.0.8", + "genkit-cli": "^1.8.0", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..1a69fd2 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/src/ai/dev.ts b/src/ai/dev.ts new file mode 100644 index 0000000..51e556a --- /dev/null +++ b/src/ai/dev.ts @@ -0,0 +1 @@ +// Flows will be imported for their side effects in this file. diff --git a/src/ai/genkit.ts b/src/ai/genkit.ts new file mode 100644 index 0000000..cbf2594 --- /dev/null +++ b/src/ai/genkit.ts @@ -0,0 +1,7 @@ +import {genkit} from 'genkit'; +import {googleAI} from '@genkit-ai/googleai'; + +export const ai = genkit({ + plugins: [googleAI()], + model: 'googleai/gemini-2.0-flash', +}); diff --git a/src/app/browse/components/file-list-item.tsx b/src/app/browse/components/file-list-item.tsx new file mode 100644 index 0000000..1a2d823 --- /dev/null +++ b/src/app/browse/components/file-list-item.tsx @@ -0,0 +1,50 @@ +import Link from 'next/link'; +import type { FileItem } from '@/types'; +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { format } from 'date-fns'; +import { Eye, DownloadCloud } from 'lucide-react'; +import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; + +interface FileListItemProps { + file: FileItem; +} + +function formatFileSize(bytes: number): string { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +export default function FileListItem({ file }: FileListItemProps) { + return ( + + +
+ + + {file.name} + +
+
+ +

Size: {formatFileSize(file.size)}

+

Uploaded: {format(new Date(file.uploadDate), "MMM d, yyyy 'at' h:mm a")}

+
+ + + {/* Actual download would require a different setup. This is a placeholder. */} + {/* */} + +
+ ); +} diff --git a/src/app/browse/components/file-list.tsx b/src/app/browse/components/file-list.tsx new file mode 100644 index 0000000..12521c6 --- /dev/null +++ b/src/app/browse/components/file-list.tsx @@ -0,0 +1,16 @@ +import type { FileItem } from '@/types'; +import FileListItem from './file-list-item'; + +interface FileListProps { + files: FileItem[]; +} + +export default function FileList({ files }: FileListProps) { + return ( +
+ {files.map((file) => ( + + ))} +
+ ); +} diff --git a/src/app/browse/page.tsx b/src/app/browse/page.tsx new file mode 100644 index 0000000..fb8cf69 --- /dev/null +++ b/src/app/browse/page.tsx @@ -0,0 +1,37 @@ + +import { getFiles } from '@/lib/file-service'; +import FileList from './components/file-list'; +import { Button } from '@/components/ui/button'; +import Link from 'next/link'; +import { PlusCircle } from 'lucide-react'; + +export default async function BrowsePage() { + const files = await getFiles(); + + return ( +
+
+

+ VividCDN - Your Files +

+ +
+ + {files.length === 0 ? ( +
+

No files uploaded yet.

+ +
+ ) : ( + + )} +
+ ); +} diff --git a/src/app/cdn/[fileId]/route.ts b/src/app/cdn/[fileId]/route.ts new file mode 100644 index 0000000..467af95 --- /dev/null +++ b/src/app/cdn/[fileId]/route.ts @@ -0,0 +1,52 @@ + +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { getFileById } from '@/lib/file-service'; +import fs from 'node:fs'; +import path from 'node:path'; + +export async function GET( + request: NextRequest, + { params }: { params: { fileId: string } } +) { + const fileId = params.fileId; + + if (!fileId) { + return NextResponse.json({ error: 'File ID is required' }, { status: 400 }); + } + + const fileMetadata = await getFileById(fileId); + + if (!fileMetadata) { + return NextResponse.json({ error: 'File metadata not found or ID is incorrect' }, { status: 404 }); + } + + // Construct the full path to the file in the CDN directory + // The filename on disk is [id].[extension] + const cdnDirectory = path.join(process.cwd(), 'CDN'); + const filePath = path.join(cdnDirectory, `${fileMetadata.id}.${fileMetadata.extension}`); + + if (!fs.existsSync(filePath)) { + console.error(`File not found at path: ${filePath} for ID ${fileId}`); + return NextResponse.json({ error: 'File not found in CDN storage' }, { status: 404 }); + } + + try { + const fileBuffer = fs.readFileSync(filePath); + + const headers = new Headers(); + headers.set('Content-Type', fileMetadata.type || 'application/octet-stream'); + // Use 'inline' for display in browser, 'attachment' to force download + headers.set('Content-Disposition', `inline; filename="${encodeURIComponent(fileMetadata.name)}"`); + headers.set('Content-Length', fileMetadata.size.toString()); + + return new NextResponse(fileBuffer, { + status: 200, + headers: headers, + }); + + } catch (error) { + console.error(`Error reading file ${fileMetadata.id}.${fileMetadata.extension}:`, error); + return NextResponse.json({ error: 'Error serving file' }, { status: 500 }); + } +} diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a91b8fc1d83a9f8107197809adcebf5de68c4fc8 GIT binary patch literal 248763 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 z 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�hr~?iTM>8^^Wpaw0HF!19@ivt7p= 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 E4+WwefB*mh literal 0 HcmV?d00001 diff --git a/src/app/files/[fileId]/page.tsx b/src/app/files/[fileId]/page.tsx new file mode 100644 index 0000000..04aa899 --- /dev/null +++ b/src/app/files/[fileId]/page.tsx @@ -0,0 +1,106 @@ + +import { getFileById } from '@/lib/file-service'; +import { notFound } from 'next/navigation'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import Link from 'next/link'; +import { ArrowLeft, DownloadCloud, ExternalLink } from 'lucide-react'; // Added ExternalLink +import { format } from 'date-fns'; +import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; +import { Badge } from '@/components/ui/badge'; // Added Badge + +interface FileDetailsPageProps { + params: { + fileId: string; + }; +} + +function formatFileSize(bytes: number): string { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +export default async function FileDetailsPage({ params }: FileDetailsPageProps) { + const file = await getFileById(params.fileId); + + if (!file) { + notFound(); + } + + const cdnUrl = `/cdn/${file.id}`; + // In a real app with a proper domain, you might construct the full URL: + // const fullCdnUrl = typeof window !== 'undefined' ? `${window.location.origin}${cdnUrl}` : cdnUrl; + // For server components, we can't rely on window.location.origin easily without passing it or client component tricks. + // So we'll display the relative path which is still useful. + + return ( +
+ + + +
+ +
+ {file.name} + + Unique ID: {file.id} + +
+
+
+ +
+
+ File Size: +

{formatFileSize(file.size)}

+
+
+ MIME Type: +

{file.type || 'N/A'}

+
+
+ Uploaded: +

{format(new Date(file.uploadDate), "PPPp")}

+
+
+ Extension: +

.{file.extension}

+
+
+ +
+ Direct CDN Link: +
+
+ {cdnUrl} +
+ +
+

This is the direct link to access the file content.

+

(For full URL, prefix with your application's domain, e.g., https://yourdomain.com{cdnUrl})

+
+ + +
+
+
+ ); +} + diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..4d08141 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,88 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + font-family: Arial, Helvetica, sans-serif; +} + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + .dark { + --background: 0 0% 7%; /* #121212 */ + --foreground: 0 0% 98%; /* #FAFAFA */ + --card: 0 0% 10%; /* Slightly lighter than background */ + --card-foreground: 0 0% 98%; + --popover: 0 0% 7%; + --popover-foreground: 0 0% 98%; + --primary: 174 42% 59%; /* Teal #4DB6AC */ + --primary-foreground: 0 0% 10%; /* Dark text on Teal */ + --secondary: 0 0% 15%; /* A bit lighter than background */ + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 15%; + --muted-foreground: 0 0% 60%; + --accent: 283 82% 65%; /* Electric Purple #BF5AF2 */ + --accent-foreground: 0 0% 98%; /* White text on Purple */ + --destructive: 0 70% 50%; /* A more vibrant red */ + --destructive-foreground: 0 0% 98%; + --border: 0 0% 18%; /* Slightly lighter than card/secondary */ + --input: 0 0% 18%; + --ring: 174 42% 59%; /* Teal for focus rings */ + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 174 42% 59%; /* Teal for sidebar primary */ + --sidebar-primary-foreground: 0 0% 10%; + --sidebar-accent: 283 82% 65%; /* Purple for sidebar accent */ + --sidebar-accent-foreground: 0 0% 98%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 174 42% 59%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..4f8654a --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,32 @@ +import type {Metadata} from 'next'; +import './globals.css'; +import { Toaster } from "@/components/ui/toaster"; +import Header from '@/components/core/Header'; // Adjusted path + +export const metadata: Metadata = { + title: 'VividCDN - Your Personal CDN', + description: 'Securely store and access your files with VividCDN.', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + + + +
+
+ {children} +
+ + + + ); +} diff --git a/src/app/login/components/login-form.tsx b/src/app/login/components/login-form.tsx new file mode 100644 index 0000000..cb9769b --- /dev/null +++ b/src/app/login/components/login-form.tsx @@ -0,0 +1,77 @@ +"use client"; + +import { useFormStatus } from 'react-dom'; +import { loginAction } from '@/lib/actions/auth.actions'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Terminal, LogIn, Loader2 } from "lucide-react"; +import { useEffect, useActionState } from 'react'; // Updated import +import { useToast } from '@/hooks/use-toast'; + + +function SubmitButton() { + const { pending } = useFormStatus(); + return ( + + ); +} + +export default function LoginForm() { + const initialState = { message: null }; + // Updated to useActionState + const [state, dispatch, isPending] = useActionState(loginAction, initialState); + const { toast } = useToast(); + + useEffect(() => { + if (state?.message && !state.message.includes("Invalid fields")) { // Don't toast form validation errors handled inline + toast({ + variant: "destructive", + title: "Login Failed", + description: state.message, + }); + } + }, [state, toast]); + + + return ( +
+
+ + +
+
+ + +
+ {state?.message && state.message.includes("Invalid fields") && ( + + + Error + {state.message} + + )} + + + ); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..58ec071 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,22 @@ +import LoginForm from './components/login-form'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { AppLogo } from '@/components/core/AppLogo'; + +export default function LoginPage() { + return ( +
+ + +
+ +
+ Welcome Back + Sign in to access your VividCDN account. +
+ + + +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..ebd97d7 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,15 @@ +import { redirect } from 'next/navigation'; +import { isAuthenticated } from '@/lib/auth'; + +export default async function HomePage() { + const authenticated = await isAuthenticated(); + + if (authenticated) { + redirect('/browse'); + } else { + redirect('/login'); + } + + // This part will not be reached due to redirects, but Next.js requires a return. + return null; +} diff --git a/src/app/upload/components/file-upload-form.tsx b/src/app/upload/components/file-upload-form.tsx new file mode 100644 index 0000000..2436a4e --- /dev/null +++ b/src/app/upload/components/file-upload-form.tsx @@ -0,0 +1,147 @@ + +"use client"; + +import { useRef, useState, useEffect, useActionState } from 'react'; // Updated import +import { useFormStatus } from 'react-dom'; +import { uploadFileAction } from '@/lib/actions/file.actions'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Progress } from '@/components/ui/progress'; +import { useToast } from '@/hooks/use-toast'; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { UploadCloud, CheckCircle, XCircle, Loader2, File as FileIconLucide } from "lucide-react"; + +function SubmitButton() { + const { pending } = useFormStatus(); + return ( + + ); +} + +export default function FileUploadForm() { + const initialState = { message: null, error: false, file: null }; + // Updated to useActionState + const [state, dispatch, isPending] = useActionState(uploadFileAction, initialState); + const { toast } = useToast(); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); // Mock progress + const formRef = useRef(null); + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + setSelectedFile(file); + setUploadProgress(0); // Reset progress + } else { + setSelectedFile(null); + } + }; + + // Simulate upload progress for UX + useEffect(() => { + let interval: NodeJS.Timeout; + // Check if isPending (from useActionState) is true and selectedFile exists + if (isPending && selectedFile && !state?.message) { + let progress = 0; + // Simulate progress quickly at first, then slower + interval = setInterval(() => { + progress += Math.random() * 15 + 5; // Random progress increment + if (progress >= 95 && !state?.message) { // Stall at 95% if no response yet + setUploadProgress(95); + } else if (progress <= 100) { + setUploadProgress(Math.min(progress, 100)); + } + if (progress >= 100 || state?.message) { + clearInterval(interval); + } + }, 150); + } else if (state?.message && !state.error) { + // If successful, jump to 100% + setUploadProgress(100); + } else if (state?.message && state.error) { + // If error, reset progress or show error state (e.g., 0 or specific error indicator) + setUploadProgress(0); + } + return () => clearInterval(interval); + }, [isPending, selectedFile, state]); + + + useEffect(() => { + if (state?.message) { + if (state.error) { + toast({ + variant: "destructive", + title: "Upload Failed", + description: state.message, + icon: , + }); + } else { + toast({ + title: "Upload Successful!", + description: state.message, + icon: , + }); + formRef.current?.reset(); + setSelectedFile(null); + setUploadProgress(100); + // Optionally reset progress to 0 after a short delay if user stays on page + setTimeout(() => setUploadProgress(0), 2000); + } + } + }, [state, toast]); + + return ( +
+
+ +
+
+ {selectedFile ? ( +
+
+
+ + {selectedFile && (isPending || uploadProgress > 0) && ( +
+ +

+ {isPending && uploadProgress < 100 ? `${uploadProgress}% uploading...` : state?.error ? 'Error' : uploadProgress === 100 && !isPending ? 'Complete' : `${uploadProgress}%`} +

+
+ )} + + {state?.error && state.message && !isPending && ( + + + Error + {state.message} + + )} + + + + ); +} diff --git a/src/app/upload/page.tsx b/src/app/upload/page.tsx new file mode 100644 index 0000000..a550711 --- /dev/null +++ b/src/app/upload/page.tsx @@ -0,0 +1,20 @@ +import FileUploadForm from './components/file-upload-form'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { UploadCloud } from 'lucide-react'; + +export default function UploadPage() { + return ( +
+ + + + Upload File + Choose a file from your device to store in VividCDN. + + + + + +
+ ); +} diff --git a/src/components/core/AppLogo.tsx b/src/components/core/AppLogo.tsx new file mode 100644 index 0000000..b83426a --- /dev/null +++ b/src/components/core/AppLogo.tsx @@ -0,0 +1,35 @@ +import type { SVGProps } from 'react'; + +export function AppLogo(props: SVGProps) { + return ( + + + + + + + + + + + + VividCDN + + + ); +} diff --git a/src/components/core/Header.tsx b/src/components/core/Header.tsx new file mode 100644 index 0000000..1bf7089 --- /dev/null +++ b/src/components/core/Header.tsx @@ -0,0 +1,53 @@ + +import Link from 'next/link'; +import { AppLogo } from '@/components/core/AppLogo'; +import { Button } from '@/components/ui/button'; +import { isAuthenticated } from '@/lib/auth'; +import { logoutAction } from '@/lib/actions/auth.actions'; +import { UploadCloud, LogOut, LogIn, LayoutGrid } from 'lucide-react'; + +export default async function Header() { + const authenticated = await isAuthenticated(); + + return ( +
+
+ + + + +
+
+ ); +} diff --git a/src/components/icons/FileTypeIcon.tsx b/src/components/icons/FileTypeIcon.tsx new file mode 100644 index 0000000..6aa1f3e --- /dev/null +++ b/src/components/icons/FileTypeIcon.tsx @@ -0,0 +1,74 @@ +import { + File, + FileImage, + FileText, + FileVideo, + Music, // Lucide doesn't have FileAudio, Music is a good alternative + Archive, + FileCode2, // Lucide doesn't have FileCode, FileCode2 is available + FileSpreadsheet, + FileJson2, // Lucide doesn't have FileJson, FileJson2 is available + FileQuestion, + LucideProps, +} from 'lucide-react'; +import type { FileItem } from '@/types'; + +interface FileTypeIconProps extends LucideProps { + fileType: FileItem['type']; + extension: FileItem['extension']; +} + +const iconMapping: Record> = { + // MIME type based + 'image': FileImage, + 'application/pdf': FileText, + 'text': FileText, + 'video': FileVideo, + 'audio': Music, + 'application/zip': Archive, + 'application/x-rar-compressed': Archive, + 'application/x-tar': Archive, + 'application/gzip': Archive, + 'application/json': FileJson2, + 'application/vnd.ms-excel': FileSpreadsheet, + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': FileSpreadsheet, + // Extension based + 'doc': FileText, + 'docx': FileText, + 'txt': FileText, + 'md': FileText, + 'rtf': FileText, + 'csv': FileSpreadsheet, + 'xls': FileSpreadsheet, + 'xlsx': FileSpreadsheet, + 'ppt': FileText, // Placeholder, could be PresentationIcon if available + 'pptx': FileText, + 'html': FileCode2, + 'css': FileCode2, + 'js': FileCode2, + 'ts': FileCode2, + 'py': FileCode2, + 'java': FileCode2, + 'c': FileCode2, + 'cpp': FileCode2, + 'cs': FileCode2, + 'php': FileCode2, + 'rb': FileCode2, + 'go': FileCode2, + 'swift': FileCode2, + 'kt': FileCode2, + 'sql': FileCode2, +}; + +export function FileTypeIcon({ fileType, extension, className, ...props }: FileTypeIconProps) { + let IconComponent = iconMapping[extension] || iconMapping[fileType.split('/')[0]] || File; + + if (fileType.startsWith('image/')) IconComponent = FileImage; + else if (fileType.startsWith('video/')) IconComponent = FileVideo; + else if (fileType.startsWith('audio/')) IconComponent = Music; + else if (fileType === 'application/pdf') IconComponent = FileText; + else if (extension === 'unknown' && fileType === 'application/octet-stream') IconComponent = FileQuestion; + + + return ; +} diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..24c788c --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..25e7b47 --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..36496a2 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx new file mode 100644 index 0000000..3cd65cc --- /dev/null +++ b/src/components/ui/calendar.tsx @@ -0,0 +1,70 @@ +"use client" + +import * as React from "react" +import { ChevronLeft, ChevronRight } from "lucide-react" +import { DayPicker } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + ( + + ), + IconRight: ({ className, ...props }) => ( + + ), + }} + {...props} + /> + ) +} +Calendar.displayName = "Calendar" + +export { Calendar } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..f62edea --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx new file mode 100644 index 0000000..32dc873 --- /dev/null +++ b/src/components/ui/chart.tsx @@ -0,0 +1,365 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +