diff --git a/.env.example b/.env.example index 789d946..8d88136 100644 --- a/.env.example +++ b/.env.example @@ -4,14 +4,4 @@ UPLOADTHING_SECRET= UPLOADTHING_APP_ID= ADMIN_USERNAME= -ADMIN_PASSWORD= - -NEXTAUTH_SECRET= - -EMAIL= -EMAIL_PASS= - -SANITY_PROJECT_ID= - -NEXT_PUBLIC_HCAPTCHA_SITE_KEY= -HCAPTCHA_SECRET= +ADMIN_PASSWORD= \ No newline at end of file diff --git a/app/(auth)/_components/Card.tsx b/app/(auth)/_components/Card.tsx index 584104f..b603c42 100644 --- a/app/(auth)/_components/Card.tsx +++ b/app/(auth)/_components/Card.tsx @@ -3,21 +3,21 @@ import Link from "next/link"; import { FC } from "react"; interface CardProps { - title: string; - url: string; + title: string; + url: string; } const Card: FC = ({ title, url }) => { - return ( -
- -
-

{title}

- -
- -
- ); + return ( +
+ +
+

{title}

+ +
+ +
+ ); }; export default Card; diff --git a/app/(auth)/_components/Mobilenav.tsx b/app/(auth)/_components/Mobilenav.tsx index d185248..8d1a2c3 100644 --- a/app/(auth)/_components/Mobilenav.tsx +++ b/app/(auth)/_components/Mobilenav.tsx @@ -9,49 +9,49 @@ import { Menu } from "lucide-react"; import Logo from "@/components/shared/Logo"; const MobileNav = () => { - const pathname = usePathname(); - return ( -
- - - + const pathname = usePathname(); + return ( +
+ + + -
- ); + return ( +
  • + + + {link.name} + +
  • + ); + })} + + + + + +
    + ); }; export default MobileNav; diff --git a/app/(auth)/_components/Sidebar.tsx b/app/(auth)/_components/Sidebar.tsx index d568eb0..1182a99 100644 --- a/app/(auth)/_components/Sidebar.tsx +++ b/app/(auth)/_components/Sidebar.tsx @@ -1,65 +1,66 @@ "use client"; import Link from "next/link"; +import Image from "next/image"; import { usePathname } from "next/navigation"; import { AdminLinks } from "@/constants"; import Logo from "@/components/shared/Logo"; const Sidebar = () => { - const pathname = usePathname(); - return ( - <> - + + ); }; export default Sidebar; diff --git a/app/(auth)/admin/changelogs/layout.tsx b/app/(auth)/admin/changelogs/layout.tsx deleted file mode 100644 index f534f4d..0000000 --- a/app/(auth)/admin/changelogs/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Admin // Changelogs", -}; - -export default function logPages({ children }: { children: React.ReactNode }) { - return <>{children}; -} diff --git a/app/(auth)/admin/changelogs/page.tsx b/app/(auth)/admin/changelogs/page.tsx index 02a5ba3..ae89cb2 100644 --- a/app/(auth)/admin/changelogs/page.tsx +++ b/app/(auth)/admin/changelogs/page.tsx @@ -1,232 +1,123 @@ "use client"; -import React, { useEffect, useState } from "react"; -import { useForm, SubmitHandler, useFieldArray } from "react-hook-form"; +import React from "react"; +import { useForm, SubmitHandler } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; import { Button } from "@/components/ui/button"; import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, } from "@/components/ui/form"; -import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; import { Input } from "@/components/ui/input"; +import { UploadButton, UploadDropzone } from "@/lib/uploadthing"; import { logsSchema } from "@/lib/validations/validation"; -import { z } from "zod"; -import { useToast } from "@/components/ui/use-toast"; -interface LogEntry { - _id: string; - version: string; - date: string; - bullets: { point: string }[]; -} +const AdminPage = () => { + const form = useForm>({ + resolver: zodResolver(logsSchema), + }); -type LogsFormValues = z.infer; + const onSubmit: SubmitHandler> = async (data) => { + const response = await fetch("/api/uploadlogs", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); -const AdminLogPage = () => { - const [logs, setLogs] = useState([]); - const [error, setError] = useState(""); - const { toast } = useToast(); - const [loading, setLoading] = useState(false); + if (response.ok) { + form.reset(); + console.log("Upload successful"); + alert("Uploaded"); + } else { + console.error("Upload failed"); + alert("Upload Failed"); + } + }; - const form = useForm({ - resolver: zodResolver(logsSchema), - defaultValues: { - version: "", - date: "", - bullets: [{ point: "" }], - }, - }); - - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: "bullets", - }); - - const fetchLogs = async () => { - try { - const response = await fetch("/api/logs", { method: "GET" }); - if (response.ok) { - const data: LogEntry[] = await response.json(); - setLogs(data); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to fetch logs"); - } - }; - - const onSubmit: SubmitHandler = async (data) => { - setLoading(true); - const response = await fetch("/api/uploadlogs", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }); - - if (response.ok) { - form.reset(); - fetchLogs(); - setLoading(false); - toast({ description: "Logs successfully added" }); - } else { - setLoading(false); - toast({ description: "Upload Failed", variant: "destructive" }); - } - }; - - const deleteLog = async (id: string) => { - try { - const response = await fetch(`/api/delete/logs/${id}`, { - method: "DELETE", - }); - if (response.ok) { - fetchLogs(); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to delete log"); - } - }; - - useEffect(() => { - fetchLogs(); - const interval = setInterval(() => { - fetchLogs(); - }, 10000); - - return () => clearInterval(interval); - }, []); - - return ( -
    -

    Server Logs Form

    -
    - - ( - - Version Name - - - - - - )} - /> - ( - - Date - - - - - - )} - /> - - {fields.map((field, index) => ( - ( - - Key Point {index + 1} - - - - - - - )} - /> - ))} - - - - - - {/* Section to list and delete logs */} -
    -

    Existing Logs

    - {error &&

    {error}

    } - - - - Version - Date - Actions - - - - {logs - .slice() - .reverse() - .map((log) => ( - - - {log.version} - - - {log.date} - - - - - - ))} - -
    -
    -
    - ); + return ( +
    +

    Admin Upload Section

    +
    + + ( + + File Name + + + + + + )} + /> + ( + + Version + + + + + + )} + /> + ( + + Download Link + { + field.onChange(res[0].url); + }} + onUploadError={(error: Error) => { + alert(`ERROR! ${error.message}`); + }} + /> + + + + + + )} + /> + ( + + File Size + + + + + + )} + /> + + + +
    + ); }; -export default AdminLogPage; +export default AdminPage; diff --git a/app/(auth)/admin/downloads/layout.tsx b/app/(auth)/admin/downloads/layout.tsx deleted file mode 100644 index 9bb582f..0000000 --- a/app/(auth)/admin/downloads/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Admin // Downloads", -}; - -export default function logPages({ children }: { children: React.ReactNode }) { - return <>{children}; -} diff --git a/app/(auth)/admin/downloads/page.tsx b/app/(auth)/admin/downloads/page.tsx index f91975f..286cfaf 100644 --- a/app/(auth)/admin/downloads/page.tsx +++ b/app/(auth)/admin/downloads/page.tsx @@ -1,251 +1,125 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React from "react"; import { useForm, SubmitHandler } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { UploadButton } from "@/lib/uploadthing"; +import { UploadButton, UploadDropzone } from "@/lib/uploadthing"; import { downloadSchema } from "@/lib/validations/validation"; -import { useToast } from "@/components/ui/use-toast"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -interface DownloadEntry { - _id: string; - fileName: string; - version: string; - downloadLink: string; - fileSize: string; -} +const AdminPage = () => { + const form = useForm>({ + resolver: zodResolver(downloadSchema), + }); -const DownloadsPage = () => { - const { toast } = useToast(); - const [downloads, setDownloads] = useState([]); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); + const onSubmit: SubmitHandler> = async ( + data + ) => { + const response = await fetch("/api/upload", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); - const form = useForm>({ - resolver: zodResolver(downloadSchema), - defaultValues: { - fileName: "", - version: "", - downloadLink: "", - fileSize: "", - }, - }); + if (response.ok) { + form.reset(); + console.log("Upload successful"); + alert("Uploaded"); + } else { + console.error("Upload failed"); + alert("Upload Failed"); + } + }; - const fetchDownloads = async () => { - try { - const response = await fetch("/api/downloads", { - method: "GET", - }); - if (response.ok) { - const data: DownloadEntry[] = await response.json(); - setDownloads(data); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to fetch downloads"); - } - }; - - useEffect(() => { - fetchDownloads(); - const interval = setInterval(() => { - fetchDownloads(); - }, 10000); - - return () => clearInterval(interval); - }, []); - - const onSubmit: SubmitHandler> = async ( - data - ) => { - setLoading(true); - const response = await fetch("/api/upload", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); - - if (response.ok) { - form.reset(); - fetchDownloads(); - setLoading(false); - toast({ description: "Download Successfully Updated" }); - } else { - console.error("Upload failed"); - setLoading(false); - toast({ description: "Uploading Failed", variant: "destructive" }); - } - }; - - const deleteDownload = async (id: string) => { - try { - const response = await fetch(`/api/delete/downloads/${id}`, { - method: "DELETE", - }); - if (response.ok) { - fetchDownloads(); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to delete download"); - } - }; - - return ( -
    -

    Downloads Form

    -
    - - ( - - File Name - - - - - - )} - /> - ( - - Version - - - - - - )} - /> - ( - - Download Link - { - field.onChange(res[0].url); - }} - onUploadError={(error: Error) => { - alert(`ERROR! ${error.message}`); - }} - /> - - - - - - )} - /> - ( - - File Size - - - - - - )} - /> - - - - - {/* Section to list and delete downloads */} -
    -

    Existing Downloads

    - {error &&

    {error}

    } - - - - File Name - Version - - Download Link - - File Size - Actions - - - - {downloads - .slice() - .reverse() - .map((download) => ( - - - {download.fileName} - - - {download.version} - - - - {download.downloadLink} - - - - {download.fileSize} - - - - - - ))} - -
    -
    -
    - ); + return ( +
    +

    Admin Upload Section

    +
    + + ( + + File Name + + + + + + )} + /> + ( + + Version + + + + + + )} + /> + ( + + Download Link + { + field.onChange(res[0].url); + }} + onUploadError={(error: Error) => { + alert(`ERROR! ${error.message}`); + }} + /> + + + + + + )} + /> + ( + + File Size + + + + + + )} + /> + + + +
    + ); }; -export default DownloadsPage; +export default AdminPage; diff --git a/app/(auth)/admin/email/page.tsx b/app/(auth)/admin/email/page.tsx deleted file mode 100644 index 43db6ed..0000000 --- a/app/(auth)/admin/email/page.tsx +++ /dev/null @@ -1,133 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { useToast } from "@/components/ui/use-toast"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { - Pagination, - PaginationContent, - PaginationItem, - PaginationLink, - PaginationNext, - PaginationPrevious, -} from "@/components/ui/pagination"; -import Link from "next/link"; - -interface Subscriber { - email: string; - subscribedAt: string; -} - -const EmailPage = () => { - const [subscribers, setSubscribers] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(0); - const { toast } = useToast(); - - useEffect(() => { - // Function to fetch subscribers data - const fetchSubscribers = async () => { - try { - const res = await fetch( - `/api/newsletter/subscriber?page=${currentPage}` - ); - const data = await res.json(); - setSubscribers(data.subscribers); - setTotalPages(data.totalPages); - } catch (error) { - toast({ - title: "Error fetching subscribers", - description: `${error}`, - }); - } - }; - - // Fetch data initially - fetchSubscribers(); - - // Set up interval to fetch data every 10 seconds - const intervalId = setInterval(fetchSubscribers, 10000); - - // Clear interval on component unmount - return () => clearInterval(intervalId); - }, [currentPage, toast]); - - return ( -
    -

    Newsletter Emails

    - - - -
    -

    Newsletter Subscribers

    -

    - Total subscribers: {subscribers.length} -

    - - - - Email - - Subscribed At - - - - - {subscribers.map((subscriber, idx) => ( - - - {subscriber.email} - - - {new Date(subscriber.subscribedAt).toLocaleDateString()} - - - ))} - -
    -
    - {totalPages > 1 && ( - - - - {currentPage > 1 && ( - setCurrentPage(currentPage - 1)} - /> - )} - - {Array.from({ length: totalPages }).map((_, i) => ( - - setCurrentPage(i + 1)} - > - {i + 1} - - - ))} - - {currentPage < totalPages && ( - setCurrentPage(currentPage + 1)} - /> - )} - - - - )} -
    -
    -
    - ); -}; - -export default EmailPage; diff --git a/app/(auth)/admin/layout.tsx b/app/(auth)/admin/layout.tsx deleted file mode 100644 index b39fafe..0000000 --- a/app/(auth)/admin/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import MobileNav from "../_components/Mobilenav"; -import Sidebar from "../_components/Sidebar"; - -export default function PageLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( -
    - - -
    {children}
    -
    - ); -} diff --git a/app/(auth)/admin/mods/layout.tsx b/app/(auth)/admin/mods/layout.tsx deleted file mode 100644 index 948a571..0000000 --- a/app/(auth)/admin/mods/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Admin // Mods", -}; - -export default function logPages({ children }: { children: React.ReactNode }) { - return <>{children}; -} diff --git a/app/(auth)/admin/mods/page.tsx b/app/(auth)/admin/mods/page.tsx index ece2940..131f3fb 100644 --- a/app/(auth)/admin/mods/page.tsx +++ b/app/(auth)/admin/mods/page.tsx @@ -1,406 +1,123 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React from "react"; import { useForm, SubmitHandler } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { UploadButton } from "@/lib/uploadthing"; +import { UploadButton, UploadDropzone } from "@/lib/uploadthing"; import { modsSchema } from "@/lib/validations/validation"; -import { useToast } from "@/components/ui/use-toast"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { - Dialog, - DialogContent, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -interface ModEntry { - _id: string; - fileName: string; - version: string; - downloadLink: string; - fileSize: string; -} +const AdminPage = () => { + const form = useForm>({ + resolver: zodResolver(modsSchema), + }); -const SvrjsModsAdminPage = () => { - const { toast } = useToast(); - const [mods, setMods] = useState([]); - const [editMod, setEditMod] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - const [dialogOpen, setDialogOpen] = useState(false); + const onSubmit: SubmitHandler> = async (data) => { + const response = await fetch("/api/uploadmods", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); - const mainForm = useForm>({ - resolver: zodResolver(modsSchema), - defaultValues: { - fileName: "", - version: "", - downloadLink: "", - fileSize: "", - }, - }); + if (response.ok) { + form.reset(); + console.log("Upload successful"); + alert("Uploaded"); + } else { + console.error("Upload failed"); + alert("Upload Failed"); + } + }; - const dialogForm = useForm>({ - resolver: zodResolver(modsSchema), - defaultValues: { - fileName: "", - version: "", - downloadLink: "", - fileSize: "", - }, - }); - - useEffect(() => { - fetchMods(); - const interval = setInterval(() => { - fetchMods(); - }, 10000); - - return () => clearInterval(interval); - }, []); - - useEffect(() => { - if (editMod) { - dialogForm.reset({ - fileName: editMod.fileName, - version: editMod.version, - downloadLink: editMod.downloadLink, - fileSize: editMod.fileSize, - }); - setDialogOpen(true); // Open dialog when a mod is being edited - } - }, [editMod]); - - const fetchMods = async () => { - try { - const response = await fetch("/api/mods", { - method: "GET", - }); - if (response.ok) { - const data: ModEntry[] = await response.json(); - setMods(data); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to fetch mods"); - } - }; - - const onSubmit: SubmitHandler> = async (data) => { - setLoading(true); - try { - const response = editMod - ? await fetch(`/api/update/mods/${editMod._id}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }) - : await fetch("/api/uploadmods", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); - - if (response.ok) { - mainForm.reset(); - dialogForm.reset(); - fetchMods(); - setLoading(false); - setEditMod(null); - setDialogOpen(false); // Close dialog on successful submission - toast({ - description: "Successfully Saved Changes", - }); - } else { - console.error("Save failed"); - setLoading(false); - toast({ - description: "Save failed", - variant: "destructive", - }); - } - } catch (error) { - console.error("Save failed", error); - setLoading(false); - toast({ - description: "Save failed", - variant: "destructive", - }); - } - }; - - const deleteMod = async (id: string) => { - try { - const response = await fetch(`/api/delete/mods/${id}`, { - method: "DELETE", - }); - if (response.ok) { - fetchMods(); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to delete mod"); - } - }; - - return ( -
    -

    Mods Form

    -
    - - ( - - File Name - - - - - - )} - /> - ( - - Version - - - - - - )} - /> - ( - - Download Link - { - field.onChange(res[0].url); - }} - onUploadError={(error: Error) => { - alert(`ERROR! ${error.message}`); - }} - /> - - - - - - )} - /> - ( - - File Size - - - - - - )} - /> - - - - - {/* Section to list and delete mods */} -
    -

    Existing Mods

    - {error &&

    {error}

    } - - - - File Name - Version - - Download Link - - File Size - Actions - - - - {mods - .slice() - .reverse() - .map((mod) => ( - - - {mod.fileName} - - - {mod.version} - - - - {mod.downloadLink} - - - - {mod.fileSize} - - - - - - - - Edit Content - -
    - - ( - - File Name - - - - - - )} - /> - ( - - Version - - - - - - )} - /> - ( - - Download Link - { - field.onChange(res[0].url); - }} - onUploadError={(error: Error) => { - alert(`ERROR! ${error.message}`); - }} - /> - - - - - - )} - /> - ( - - File Size - - - - - - )} - /> - - - -
    -
    - -
    -
    - ))} -
    -
    -
    -
    - ); + return ( +
    +

    Admin Upload Section

    +
    + + ( + + File Name + + + + + + )} + /> + ( + + Version + + + + + + )} + /> + ( + + Download Link + { + field.onChange(res[0].url); + }} + onUploadError={(error: Error) => { + alert(`ERROR! ${error.message}`); + }} + /> + + + + + + )} + /> + ( + + File Size + + + + + + )} + /> + + + +
    + ); }; -export default SvrjsModsAdminPage; +export default AdminPage; diff --git a/app/(auth)/admin/multi-logs/[slug]/page.tsx b/app/(auth)/admin/multi-logs/[slug]/page.tsx deleted file mode 100644 index f43f2ce..0000000 --- a/app/(auth)/admin/multi-logs/[slug]/page.tsx +++ /dev/null @@ -1,85 +0,0 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; -import dynamic from "next/dynamic"; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; -import { useToast } from "@/components/ui/use-toast"; - -const MarkdownEditor = dynamic(() => import("@uiw/react-md-editor"), { - ssr: false, -}); - -const EditPage = ({ params }: { params: { slug: string } }) => { - const router = useRouter(); - const { slug } = params; - const { toast } = useToast(); - const [title, setTitle] = useState(""); - const [content, setContent] = useState(""); - const [vulnerabilities, setVulnerabilities] = useState(""); - const [loading, setLoading] = useState(false); - - useEffect(() => { - if (slug) { - fetch(`/api/mdx/pages/${slug}`) - .then((response) => response.json()) - .then((data) => { - setTitle(data.title); - setContent(data.content); - setVulnerabilities(data.vulnerabilities || ""); - }) - .catch((error) => console.error("Failed to load page", error)); - } - }, [slug]); - - const savePage = async () => { - setLoading(true); - const response = await fetch(`/api/mdx/pages/${slug}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ title, content, vulnerabilities }), - }); - - if (response.ok) { - setLoading(false); - toast({ description: "Page successfully updated" }); - router.push(`/admin/multi-logs/`); - } else { - setLoading(false); - toast({ description: "Page Updated" }); - } - }; - - const handleEditorChange = (value?: string) => { - if (value !== undefined) { - setContent(value); - } - }; - - return ( -
    -

    Edit Page: {slug}

    - setTitle(e.target.value)} - placeholder="Page Title" - /> - -

    Vulnerabilities

    - setVulnerabilities(value || "")} - height={200} - /> - -
    - ); -}; - -export default EditPage; diff --git a/app/(auth)/admin/multi-logs/layout.tsx b/app/(auth)/admin/multi-logs/layout.tsx deleted file mode 100644 index 8c4c850..0000000 --- a/app/(auth)/admin/multi-logs/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Admin // MultiLogs", -}; - -export default function logPages({ children }: { children: React.ReactNode }) { - return <>{children}; -} diff --git a/app/(auth)/admin/multi-logs/page.tsx b/app/(auth)/admin/multi-logs/page.tsx deleted file mode 100644 index 9f1c6c1..0000000 --- a/app/(auth)/admin/multi-logs/page.tsx +++ /dev/null @@ -1,158 +0,0 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { Button } from "@/components/ui/button"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { Input } from "@/components/ui/input"; -import { useRouter } from "next/navigation"; -import { useToast } from "@/components/ui/use-toast"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, -} from "@/components/ui/dialog"; - -interface PageEntry { - title: string; - slug: string; - content: string; -} - -const MultiLogs = () => { - const [pages, setPages] = useState([]); - const { toast } = useToast(); - const router = useRouter(); - const [loading, setLoading] = useState(false); - const [open, setOpen] = useState(false); - const [pageTitle, setPageTitle] = useState(""); - - useEffect(() => { - fetch("/api/mdx/pages") - .then((response) => response.json()) - .then((data) => setPages(data)) - .catch((error) => console.error("Failed to load pages", error)); - }, []); - - const createPage = async () => { - setLoading(true); - const slug = pageTitle.toLowerCase().replace(/\s+/g, "-"); - const response = await fetch("/api/mdx/pages", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ title: pageTitle, slug, content: "" }), - }); - - if (response.ok) { - const newPage = await response.json(); - setPages([...pages, newPage]); - setPageTitle(""); - setOpen(false); - router.push(`/admin/multi-logs/${slug}`); - toast({ description: "Page created successfully" }); - } else { - const errorData = await response.json(); - console.error("Failed to create page:", errorData); - toast({ description: `Error: ${errorData.message}` }); - } - setLoading(false); - }; - - const deletePage = async (slug: string) => { - setLoading(true); - const response = await fetch(`/api/mdx/pages/${slug}`, { - method: "DELETE", - }); - - if (response.ok) { - setPages(pages.filter((page) => page.slug !== slug)); - toast({ description: "Page deleted successfully" }); - } else { - const errorData = await response.json(); - console.error("Failed to delete page:", errorData); - toast({ description: `Error: ${errorData.message}` }); - } - setLoading(false); - }; - - return ( -
    -
    -

    Create New Page

    - - - - - Enter Page Title - - setPageTitle(e.target.value)} - placeholder="Page Title" - /> - - - - - -
    -
    -

    Existing Pages

    -

    Total Pages: {pages.length}

    - - - - Slug - Actions - - - - {pages.map((page) => ( - - - - {page.slug} - - - - - - - - ))} - -
    -
    -
    - ); -}; - -export default MultiLogs; diff --git a/app/(auth)/admin/page.tsx b/app/(auth)/admin/page.tsx index e27ee67..0ede98b 100644 --- a/app/(auth)/admin/page.tsx +++ b/app/(auth)/admin/page.tsx @@ -1,21 +1,21 @@ import React from "react"; import Card from "../_components/Card"; -import { AdminDashboardLINKS } from "@/constants"; const AdminPage = () => { - return ( - <> -
    -

    Admin Page

    + return ( + <> +
    +

    Admin Page

    -
    - {AdminDashboardLINKS.map((item, idx) => ( - - ))} -
    -
    - - ); +
    + + + + +
    +
    + + ); }; export default AdminPage; diff --git a/app/(auth)/admin/vulnerabilities/layout.tsx b/app/(auth)/admin/vulnerabilities/layout.tsx deleted file mode 100644 index 2181c4a..0000000 --- a/app/(auth)/admin/vulnerabilities/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Admin // Vulnerabilities", -}; - -export default function logPages({ children }: { children: React.ReactNode }) { - return <>{children}; -} diff --git a/app/(auth)/admin/vulnerabilities/page.tsx b/app/(auth)/admin/vulnerabilities/page.tsx deleted file mode 100644 index 65a3e3e..0000000 --- a/app/(auth)/admin/vulnerabilities/page.tsx +++ /dev/null @@ -1,221 +0,0 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { useForm, SubmitHandler, useFieldArray } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { Input } from "@/components/ui/input"; -import { z } from "zod"; -import { useToast } from "@/components/ui/use-toast"; -import { vulnerabilitiesSchema } from "@/lib/validations/validation"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, -} from "@/components/ui/select"; - -interface VulnerabiltyEntry { - _id: string; - version: string; - bullets: { point: string }[]; -} - -type VulnerabiltiesForm = z.infer; - -const AdminLogPage = () => { - const [logs, setLogs] = useState([]); - const [error, setError] = useState(""); - const { toast } = useToast(); - const [loading, setLoading] = useState(false); - - const form = useForm({ - resolver: zodResolver(vulnerabilitiesSchema), - defaultValues: { - version: "", - bullets: [{ point: "" }], - }, - }); - - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: "bullets", - }); - - const fetchLogs = async () => { - try { - const response = await fetch("/api/vulnerabilities", { method: "GET" }); - if (response.ok) { - const data: VulnerabiltyEntry[] = await response.json(); - setLogs(data); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to fetch logs"); - } - }; - - const onSubmit: SubmitHandler = async (data) => { - setLoading(true); - const response = await fetch("/api/uploadvulnerabilities", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }); - - if (response.ok) { - form.reset(); - fetchLogs(); - setLoading(false); - toast({ description: "Logs successfully added" }); - } else { - setLoading(false); - toast({ description: "Upload Failed", variant: "destructive" }); - } - }; - - const deleteLog = async (id: string) => { - try { - const response = await fetch(`/api/delete/vulnerability/${id}`, { - method: "DELETE", - }); - if (response.ok) { - fetchLogs(); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to delete log"); - } - }; - - useEffect(() => { - fetchLogs(); - const interval = setInterval(() => { - fetchLogs(); - }, 10000); - - return () => clearInterval(interval); - }, []); - - return ( -
    -

    Server Vulnerabilties Form

    -
    - - ( - - Version Name - - - - - - )} - /> - - {fields.map((field, index) => ( - ( - - Key Point {index + 1} - - - - - - - )} - /> - ))} - - - - - - {/* Section to list and delete logs */} -
    -

    - Existing Vulnerabilties -

    - {error &&

    {error}

    } - - - - Version - Actions - - - - {logs - .slice() - .reverse() - .map((log) => ( - - - {log.version} - - - - - - ))} - -
    -
    -
    - ); -}; - -export default AdminLogPage; diff --git a/app/(auth)/email-editor/page.tsx b/app/(auth)/email-editor/page.tsx deleted file mode 100644 index 9c53874..0000000 --- a/app/(auth)/email-editor/page.tsx +++ /dev/null @@ -1,147 +0,0 @@ -"use client"; - -import React, { useState } from "react"; -import Link from "next/link"; -import { Button } from "@/components/ui/button"; -import CodeEditor from "@/components/cards/MonacoEditor"; -import { EXAMPLE_A1 } from "@/constants"; -import { useToast } from "@/components/ui/use-toast"; - -const EmailEditor = () => { - const { toast } = useToast(); - const [subject, setSubject] = useState(""); - const [previewContent, setPreviewContent] = useState(EXAMPLE_A1); - const [loading, setLoading] = useState(false); - - const validateInputs = () => { - if (!subject.trim() || !previewContent.trim()) { - toast({ - title: "Validation Error", - description: "Subject and content cannot be empty.", - variant: "destructive", - }); - return false; - } - return true; - }; - - const handleSendAll = async () => { - if (!validateInputs()) return; - - setLoading(true); - try { - const response = await fetch("/api/newsletter/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - subject: subject, - html: previewContent, - }), - }); - - if (!response.ok) { - throw new Error("Network response was not ok"); - } - - const result = await response.json(); - toast({ - title: "Success!", - description: result.message || "Emails sent successfully", - }); - } catch (error) { - console.error("Error:", error); - toast({ - title: "Uh oh!", - description: `Failed to send emails: ${error}`, - variant: "destructive", - }); - } finally { - setLoading(false); - } - }; - - const handleSendTest = async () => { - if (!validateInputs()) return; - - setLoading(true); - try { - const response = await fetch("/api/newsletter/test", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - subject: subject, - html: previewContent, - }), - }); - - if (!response.ok) { - throw new Error("Network response was not ok"); - } - - const result = await response.json(); - toast({ - title: "Success!", - description: result.message || "Test email sent successfully", - }); - } catch (error) { - console.error("Error:", error); - toast({ - title: "Uh oh!", - description: `Failed to send test email: ${error}`, - variant: "destructive", - }); - } finally { - setLoading(false); - } - }; - - const handleEditorChange = (value: string) => { - setPreviewContent(value); - }; - - return ( -
    -
    - - Back - - setSubject(e.target.value)} - className="border rounded-md p-2" - /> - -
    - - -
    -
    - -
    -

    - Email Preview -

    -
    -
    -
    - ); -}; - -export default EmailEditor; diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx new file mode 100644 index 0000000..b6569b8 --- /dev/null +++ b/app/(auth)/layout.tsx @@ -0,0 +1,16 @@ +import MobileNav from "./_components/Mobilenav"; +import Sidebar from "./_components/Sidebar"; + +export default function PageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
    + + +
    {children}
    +
    + ); +} diff --git a/app/(root)/blog/[slug]/_styles/prism-twilight.css b/app/(root)/blog/[slug]/_styles/prism-twilight.css deleted file mode 100644 index 650904b..0000000 --- a/app/(root)/blog/[slug]/_styles/prism-twilight.css +++ /dev/null @@ -1,123 +0,0 @@ -/** - * okaidia theme for JavaScript, CSS and HTML - * Loosely based on Monokai textmate theme by http://www.monokai.nl/ - * @author ocodia - */ - -code[class*="language-"], -pre[class*="language-"] { - color: #f8f8f2; - background: none; - text-shadow: 0 1px rgba(0, 0, 0, 0.3); - font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - font-size: 1em; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: 0.5em 0; - overflow: auto; - border-radius: 0.3em; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #272822; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: 0.1em; - border-radius: 0.3em; - white-space: normal; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: #8292a2; -} - -.token.punctuation { - color: #f8f8f2; -} - -.token.namespace { - opacity: 0.7; -} - -.token.property, -.token.tag, -.token.constant, -.token.symbol, -.token.deleted { - color: #f92672; -} - -.token.boolean, -.token.number { - color: #ae81ff; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.char, -.token.builtin, -.token.inserted { - color: #a6e22e; -} - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable { - color: #f8f8f2; -} - -.token.atrule, -.token.attr-value, -.token.function, -.token.class-name { - color: #e6db74; -} - -.token.keyword { - color: #66d9ef; -} - -.token.regex, -.token.important { - color: #fd971f; -} - -.token.important, -.token.bold { - font-weight: bold; -} -.token.italic { - font-style: italic; -} - -.token.entity { - cursor: help; -} diff --git a/app/(root)/blog/[slug]/_styles/prism.twilight.min.css b/app/(root)/blog/[slug]/_styles/prism.twilight.min.css deleted file mode 100644 index 6771ee8..0000000 --- a/app/(root)/blog/[slug]/_styles/prism.twilight.min.css +++ /dev/null @@ -1,98 +0,0 @@ -code[class*="language-"], -pre[class*="language-"] { - color: #f8f8f2; - background: 0 0; - text-shadow: 0 1px rgba(0, 0, 0, 0.3); - font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - font-size: 1em; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} -pre[class*="language-"] { - padding: 1em; - margin: 0.5em 0; - overflow: auto; - border-radius: 0.3em; -} -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #272822; -} -:not(pre) > code[class*="language-"] { - padding: 0.1em; - border-radius: 0.3em; - white-space: normal; -} -.token.cdata, -.token.comment, -.token.doctype, -.token.prolog { - color: #8292a2; -} -.token.punctuation { - color: #f8f8f2; -} -.token.namespace { - opacity: 0.7; -} -.token.constant, -.token.deleted, -.token.property, -.token.symbol, -.token.tag { - color: #f92672; -} -.token.boolean, -.token.number { - color: #ae81ff; -} -.token.attr-name, -.token.builtin, -.token.char, -.token.inserted, -.token.selector, -.token.string { - color: #a6e22e; -} -.language-css .token.string, -.style .token.string, -.token.entity, -.token.operator, -.token.url, -.token.variable { - color: #f8f8f2; -} -.token.atrule, -.token.attr-value, -.token.class-name, -.token.function { - color: #e6db74; -} -.token.keyword { - color: #66d9ef; -} -.token.important, -.token.regex { - color: #fd971f; -} -.token.bold, -.token.important { - font-weight: 700; -} -.token.italic { - font-style: italic; -} -.token.entity { - cursor: help; -} diff --git a/app/(root)/blog/[slug]/page.tsx b/app/(root)/blog/[slug]/page.tsx deleted file mode 100644 index b5ffe76..0000000 --- a/app/(root)/blog/[slug]/page.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { client, urlFor } from "@/lib/sanity"; -import { PortableText, PortableTextComponents } from "@portabletext/react"; -import Image from "next/image"; -import Link from "next/link"; -import { ArrowLeft, Rss } from "lucide-react"; -import { Separator } from "@/components/ui/separator"; -import { notFound } from "next/navigation"; -import { Metadata } from "next"; -import { format } from "date-fns"; -import Prism from "prismjs"; -import "prismjs/components/prism-javascript"; -import "prismjs/components/prism-python"; -import "prismjs/components/prism-php"; - -import CopyButton from "@/components/shared/copyButton"; -import "./_styles/prism-twilight.css"; -import "./_styles/prism.twilight.min.css"; -import PrismLoader from "@/components/loader/prismLoader"; -import { Button } from "@/components/ui/button"; - -async function getData(slug: string) { - const query = ` - *[_type == "blog" && slug.current == '${slug}'] { - "currentSlug": slug.current, - title, - content, - titleImage, - _createdAt - }[0]`; - - const data = await client.fetch(query); - return data; -} - -interface BlogSlugArticle { - currentSlug: string; - title: string; - content: any; - titleImage: string; - _createdAt: string; -} - -export async function generateMetadata({ - params, -}: { - params: { slug: string }; -}): Promise { - const data = await getData(params.slug); - - if (!data) { - return { - title: "Not Found", - description: "Blog post not found", - }; - } - - return { - title: `${data.title} - SVRJS`, - description: data.smallDescription, - openGraph: { - title: `${data.title} - SVRJS`, - description: data.smallDescription, - url: `https://svrjs.org/blog/${data.currentSlug}`, - type: "website", - images: [ - { - url: urlFor(data.titleImage).url(), - width: 800, - height: 600, - alt: `${data.title} - SVRJS`, - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@SVR_JS", - title: `${data.title} - SVRJS`, - description: data.smallDescription, - images: [urlFor(data.titleImage).url()], - creator: "@SVR_JS", - }, - }; -} - -const customPortableTextComponents: PortableTextComponents = { - types: { - image: ({ value }) => { - return ( -
    - {value.alt - {value.caption && ( -

    - {value.caption} -

    - )} -
    - ); - }, - code: ({ value }) => { - const language = value.language || "javascript"; - const grammar = Prism.languages[language]; - - if (!grammar) { - console.error(`No grammar found for language: "${language}"`); - return ( -
    -						{value.code}
    -					
    - ); - } - - return ( -
    -
    -						{value.code}
    -					
    - - -
    - ); - }, - }, -}; - -export default async function BlogSlugArticle({ - params, -}: { - params: { slug: string }; -}) { - const data: BlogSlugArticle = await getData(params.slug); - - if (!data) { - notFound(); - } - - const formattedDate = format(new Date(data._createdAt), "MMMM d, yyyy"); - - return ( - <> -
    -
    - - - - - - -
    -
    - {data.titleImage && ( -
    -

    - {data.title} -

    - {data.title} -

    - Uploaded at {formattedDate} -

    -
    - )} -
    - -
    - -
    -
    - - ); -} diff --git a/app/(root)/blog/page.tsx b/app/(root)/blog/page.tsx index a0d089f..fc1f38e 100644 --- a/app/(root)/blog/page.tsx +++ b/app/(root)/blog/page.tsx @@ -1,66 +1,11 @@ import React from "react"; import { Metadata } from "next"; -import BlogCards from "@/components/cards/BlogCards"; -import { Rss } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import Link from "next/link"; export const metadata: Metadata = { - title: "Blog - SVRJS", - description: - "Welcome to the SVR.JS Blog! Explore our latest blog posts featuring web development, web application security, and web server administration tips. Stay tuned for the latest SVR.JS updates.", - openGraph: { - title: "Blog - SVRJS", - description: - "Welcome to the SVR.JS Blog! Explore our latest blog posts featuring web development, web application security, and web server administration tips. Stay tuned for the latest SVR.JS updates.", - url: "https://svrjs.org/blog", - type: "website", - images: [ - { - url: "https://svrjs.vercel.app/metadata/svrjs-cover.png", - width: 800, - height: 600, - alt: "Blog - SVRJS", - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@SVR_JS", - title: "Blog - SVRJS", - description: - "Welcome to the SVR.JS Blog! Explore our latest blog posts featuring web development, web application security, and web server administration tips. Stay tuned for the latest SVR.JS updates.", - images: ["https://svrjs.vercel.app/metadata/svrjs-cover.png"], - creator: "@SVR_JS", - }, + title: "Blog - SVRJS", }; - -const BlogPage = async ({ - searchParams, -}: { - searchParams: { page?: string }; -}) => { - // Optionally, you can fetch some initial data here if needed. - - return ( -
    -

    - SVRJS Blog Post -

    -

    - Stay updated with our latest blog posts by subscribing to our - - - -

    - -
    - ); +const BlogPage = () => { + return
    BlogPage
    ; }; export default BlogPage; diff --git a/app/(root)/changelogs/[slug]/page.tsx b/app/(root)/changelogs/[slug]/page.tsx deleted file mode 100644 index 6cd909f..0000000 --- a/app/(root)/changelogs/[slug]/page.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; -import { Skeleton } from "@/components/ui/skeleton"; -import React, { useEffect, useState } from "react"; -import ReactMarkdown from "react-markdown"; -import Head from "next/head"; - -const Page = ({ params }: { params: { slug: string } }) => { - const { slug } = params; - const [page, setPage] = useState<{ title: string; content: string } | null>( - null - ); - const [loading, setLoading] = useState(true); - const [notFound, setNotFound] = useState(false); - - useEffect(() => { - const fetchPage = async () => { - try { - const response = await fetch(`/api/mdx/pages/${slug}`); - if (response.ok) { - const data = await response.json(); - setPage(data); - return (document.title = `${data.title} Change Log - SVRJS`); - } else { - if (response.status === 404) { - setNotFound(true); - return (document.title = "404 Not Found"); - } - } - } catch (error) { - console.error("Failed to load page", error); - setNotFound(true); - } finally { - setLoading(false); - } - }; - - fetchPage(); - }, [slug]); - - if (loading) { - return ( - <> - - Mods Change Logs - SVRJS - -
    -
    - -
    -
    - - - - -
    -
    - - ); - } - - if (notFound) { - return ( -
    -

    - 404 Page not Found -

    -

    - Please return back to Home -

    -
    - ); - } - - if (!page) { - return null; - } - - return ( - <> - - {page.title} Changelog - SVRJS - - - - - - - - - Documentation - SVRJS - - - - - - -
    -

    - {page.title} Change Log -

    - - {page.content} - -
    - - ); -}; - -export default Page; diff --git a/app/(root)/changelogs/layout.tsx b/app/(root)/changelogs/layout.tsx deleted file mode 100644 index 6dd0e52..0000000 --- a/app/(root)/changelogs/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Metadata } from "next"; - -// baseURL [ENV] -export const metadata: Metadata = { - title: "ChangeLogs - SVRJS", - description: - "Stay up-to-date with the latest improvements and updates to SVR.JS web server. Our change log page provides a comprehensive list of new features, bug fixes, and enhancements for each release.", - openGraph: { - title: "ChangeLogs - SVRJS", - description: - "Stay up-to-date with the latest improvements and updates to SVR.JS web server. Our change log page provides a comprehensive list of new features, bug fixes, and enhancements for each release.", - url: "https://svrjs.org/changelogs", - type: "website", - images: [ - { - url: "https://svrjs.vercel.app/metadata/svrjs-cover.png", - width: 800, - height: 600, - alt: "ChangeLogs - SVRJS", - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@SVR_JS", - title: "ChangeLogs - SVRJS", - description: - "Stay up-to-date with the latest improvements and updates to SVR.JS web server. Our change log page provides a comprehensive list of new features, bug fixes, and enhancements for each release.", - images: ["https://svrjs.vercel.app/metadata/svrjs-cover.png"], - creator: "@SVR_JS", - }, -}; -const ContactLayout = ({ children }: { children: React.ReactNode }) => { - return
    {children}
    ; -}; - -export default ContactLayout; diff --git a/app/(root)/changelogs/page.tsx b/app/(root)/changelogs/page.tsx index de52650..685d275 100644 --- a/app/(root)/changelogs/page.tsx +++ b/app/(root)/changelogs/page.tsx @@ -2,110 +2,91 @@ import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; import { Download } from "lucide-react"; import Link from "next/link"; -import ReactMarkdown from "react-markdown"; -import { CHANGE_LOGS } from "@/constants/guidelines"; -import { Skeleton } from "@/components/ui/skeleton"; - -interface Bullet { - point: string; -} interface LOGS { - _id: string; - date: string; - version: string; - bullets?: Bullet[]; // Make bullets optional + _id: string; + date: string; + fileName: string; + version: string; + fileSize: string; + downloadLink: string; } -const LogsPage: React.FC = () => { - const [downloads, setDownloads] = useState([]); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(true); +const DownloadPage: React.FC = () => { + const [downloads, setDownloads] = useState([]); + const [error, setError] = useState(""); - const fetchDownloads = async () => { - try { - const response = await fetch("/api/logs", { - method: "GET", - }); - if (response.ok) { - const data: LOGS[] = await response.json(); - setDownloads(data); - } else { - throw new Error(`HTTP error! status: ${response.status}`); - } - } catch (error: any) { - setError(error.message || "Failed to fetch downloads"); - } finally { - setLoading(false); - } - }; + const fetchDownloads = async () => { + try { + const response = await fetch("/api/logs", { + method: "GET", + }); + if (response.ok) { + const data: LOGS[] = await response.json(); + setDownloads(data); + } else { + throw new Error(`HTTP error! status: ${response.status}`); + } + } catch (error: any) { + setError(error); + } + }; - useEffect(() => { - fetchDownloads(); + useEffect(() => { + fetchDownloads(); - const interval = setInterval(() => { - fetchDownloads(); - }, 10000); + const interval = setInterval(() => { + fetchDownloads(); + }, 10000); - return () => clearInterval(interval); - }, []); - const reversedDownloads = [...downloads].reverse(); + return () => clearInterval(interval); + }, []); - if (loading) { - return ( - <> - - Change Logs - SVRJS - -
    -
    - -
    -
    - - - - -
    -
    - - ); - } + return ( +
    +

    + Server LOGS +

    +

    + Get all the latest version of SVRJS download and compiled Files here! +

    + {error &&

    {error}

    } - return ( -
    -

    - Server LOGS -

    -

    - Get all the latest version of SVRJS download and compiled Files here! -

    - {error &&

    {error}

    } - - {reversedDownloads.map((download) => ( -
    -

    {download.version}

    - {download.date} -
      - {(download.bullets ?? []).map((bullet, index) => ( -
    • {bullet.point}
    • - ))} -
    -
    - ))} -
    - {CHANGE_LOGS} -
    -
    - ); + {downloads + .slice(0, 10) + .reverse() + .map((download) => ( +
    + {download.date} + {download.fileName} + {download.version} + {download.fileSize} + + + + + +
    + ))} +
    + ); }; -export default LogsPage; +export default DownloadPage; diff --git a/app/(root)/contact/layout.tsx b/app/(root)/contact/layout.tsx deleted file mode 100644 index b2e614c..0000000 --- a/app/(root)/contact/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Metadata } from "next"; - -// baseURL [ENV] -export const metadata: Metadata = { - title: "Contact Us - SVRJS", - description: - "Have questions about SVR.JS? Need technical support? Visit our Contact Us page to find various ways to get in touch with our team, including email, forums, and our official support channel.", - openGraph: { - title: "Contact Us - SVRJS", - description: - "Have questions about SVR.JS? Need technical support? Visit our Contact Us page to find various ways to get in touch with our team, including email, forums, and our official support channel.", - url: "https://svrjs.org/contact", - type: "website", - images: [ - { - url: "https://svrjs.vercel.app/metadata/svrjs-cover.png", - width: 800, - height: 600, - alt: "Contact Us - SVRJS", - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@SVR_JS", - title: "Contact Us - SVRJS", - description: - "Have questions about SVR.JS? Need technical support? Visit our Contact Us page to find various ways to get in touch with our team, including email, forums, and our official support channel.", - images: ["https://svrjs.vercel.app/metadata/svrjs-cover.png"], - creator: "@SVR_JS", - }, -}; -const ContactLayout = ({ children }: { children: React.ReactNode }) => { - return
    {children}
    ; -}; - -export default ContactLayout; diff --git a/app/(root)/contact/page.tsx b/app/(root)/contact/page.tsx deleted file mode 100644 index 8eeab8b..0000000 --- a/app/(root)/contact/page.tsx +++ /dev/null @@ -1,201 +0,0 @@ -"use client"; -import Iconss from "@/components/ui/icons"; -import { Mail, Send, WebhookIcon, Bug, Shield } from "lucide-react"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { contactFormSchema } from "@/lib/validations/validation"; -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { useToast } from "@/components/ui/use-toast"; -import { useState } from "react"; -import { Separator } from "@/components/ui/separator"; -import { emails } from "@/constants"; -import HCaptcha from "@hcaptcha/react-hcaptcha"; - -const ContactUs = () => { - const { toast } = useToast(); - const [loading, setLoading] = useState(false); - const [showCaptcha, setShowCaptcha] = useState(false); - const [captchaToken, setCaptchaToken] = useState(null); - - const form = useForm>({ - resolver: zodResolver(contactFormSchema), - defaultValues: { - name: "", - email: "", - message: "", - }, - }); - - async function onSubmit(values: z.infer) { - if (!captchaToken) { - setShowCaptcha(true); - return; - } - - setLoading(true); - try { - const res = await fetch("/api/contact", { - method: "POST", - body: JSON.stringify({ ...values, captchaToken }), - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }); - - if (res.ok) { - form.reset(); - setCaptchaToken(null); // Reset captcha token after successful submission - toast({ - description: "Your message has been sent.", - }); - } else { - toast({ - title: "Uh oh! Something went wrong.", - variant: "destructive", - }); - } - } catch (error) { - console.error(error); - toast({ - title: "Uh oh! Something went wrong.", - variant: "destructive", - }); - } finally { - setLoading(false); - setShowCaptcha(false); // Hide captcha after submission attempt - } - } - - function handleCaptchaVerify(token: string) { - setCaptchaToken(token); - onSubmit(form.getValues()); // Trigger form submission after captcha is verified - } - - return ( - <> -
    -

    - Contact Us -

    -
    - -
    -
    - {/* Left contact page */} -
    - - ( - - Name - - - - - - )} - /> - ( - - Email - - - - - - )} - /> - ( - - Message - -