BIG UPDATE: changed logic so only 1 network req is needed to get all scripts.
This commit is contained in:
@@ -1,24 +1,59 @@
|
||||
'use client'
|
||||
import ScriptItem from "@/components/Script";
|
||||
import ScriptBrowser from "@/components/ScriptBrowser";
|
||||
import Particles from "@/components/ui/particles";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { Category } from "@/lib/types";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Page() {
|
||||
const [links, setLinks] = useState<Category[]>([]);
|
||||
const [isLoading , setIsLoading] = useState(true)
|
||||
|
||||
const fetchLinks = async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const res = await pb.collection("categories").getFullList({
|
||||
expand: "items",
|
||||
sort: "order",
|
||||
requestKey: "desktop",
|
||||
});
|
||||
setLinks(res as unknown as Category[]);
|
||||
setIsLoading(false)
|
||||
} catch (error) {
|
||||
console.error("Error fetching links:", error);
|
||||
setIsLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchLinks();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Particles
|
||||
className="animate-fade-in absolute inset-0 -z-10"
|
||||
quantity={100}
|
||||
/>
|
||||
<div className="min-h-screen mb-3">
|
||||
<div className="mt-20 flex sm:px-7 xl:px-0">
|
||||
<div className="hidden sm:flex">
|
||||
<ScriptBrowser />
|
||||
{!isLoading ? (
|
||||
<>
|
||||
<Particles
|
||||
className="animate-fade-in absolute inset-0 -z-10"
|
||||
quantity={100}
|
||||
/>
|
||||
<div className="min-h-screen mb-3">
|
||||
<div className="mt-20 flex sm:px-7 xl:px-0">
|
||||
<div className="hidden sm:flex">
|
||||
<ScriptBrowser items={links} />
|
||||
</div>
|
||||
<div className="mx-7 w-full sm:mx-0 sm:ml-7">
|
||||
<ScriptItem items={links} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-7 w-full sm:mx-0 sm:ml-7">
|
||||
<ScriptItem />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<div className="animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,27 +8,24 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Script } from "@/lib/types";
|
||||
import { Category, Script } from "@/lib/types";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Button } from "./ui/button";
|
||||
import { extractDate } from "@/lib/time";
|
||||
|
||||
function LatestScripts() {
|
||||
function LatestScripts({items } : {items: Category[]}) {
|
||||
const [latestScripts, setLatestScripts] = useState<Script[]>([]);
|
||||
|
||||
async function getLatestScripts() {
|
||||
const res = await pb.collection("proxmox_scripts").getList(1, 3, {
|
||||
sort: "-created",
|
||||
requestKey: "latest_scripts",
|
||||
});
|
||||
|
||||
setLatestScripts(res.items as unknown as Script[]);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getLatestScripts();
|
||||
}, []);
|
||||
if (items) {
|
||||
const scripts = items.flatMap((category) => category.expand.items);
|
||||
const sortedScripts = scripts.sort((a, b) => {
|
||||
return new Date(b.created).getTime() - new Date(a.created).getTime();
|
||||
});
|
||||
setLatestScripts(sortedScripts.slice(0, 3));
|
||||
}
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
|
||||
@@ -8,32 +8,25 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Script } from "@/lib/types";
|
||||
import { Category, Script } from "@/lib/types";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Button } from "./ui/button";
|
||||
import { extractDate } from "@/lib/time";
|
||||
|
||||
export default function MostViewedScripts() {
|
||||
export default function MostViewedScripts({items} : {items: Category[]}) {
|
||||
const [latestScripts, setLatestScripts] = useState<Script[]>([]);
|
||||
|
||||
async function getLatestScripts() {
|
||||
try {
|
||||
const res = await pb.collection("proxmox_scripts").getList(1, 3, {
|
||||
sort: "mostViewedPosition",
|
||||
filter: "isMostViewed = true",
|
||||
});
|
||||
setLatestScripts(res.items as unknown as Script[]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching scripts:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getLatestScripts();
|
||||
}, []);
|
||||
if (items) {
|
||||
const scripts = items.flatMap((category) => category.expand.items);
|
||||
const mostViewedScripts = scripts.filter((item) => item.isMostViewed);
|
||||
const sortedScripts = mostViewedScripts.sort((a, b) => {
|
||||
return a.mostViewedPosition - b.mostViewedPosition;
|
||||
});
|
||||
setLatestScripts(sortedScripts.slice(0, 3));
|
||||
}
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
|
||||
@@ -8,28 +8,25 @@ import { toast } from "sonner";
|
||||
import { Clipboard, Info, X } from "lucide-react";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import Link from "next/link";
|
||||
import { Script } from "@/lib/types";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Category, Script } from "@/lib/types";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import LatestScripts from "./LatestScripts";
|
||||
import MostViewedScripts from "./MostViewedScripts";
|
||||
|
||||
function ScriptItem() {
|
||||
function ScriptItem({ items }: { items: Category[] }) {
|
||||
const [item, setItem] = useState<Script | null>(null);
|
||||
const id = useSearchParams().get("id");
|
||||
|
||||
const getItem = async () => {
|
||||
const res = await pb.collection('proxmox_scripts').getFirstListItem(`title="${id}"`);
|
||||
setItem(res as unknown as Script);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
getItem();
|
||||
} else {
|
||||
setItem(null);
|
||||
if (items) {
|
||||
const script = items
|
||||
.map((category) => category.expand.items)
|
||||
.flat()
|
||||
.find((script) => script.title === id);
|
||||
setItem(script || null);
|
||||
}
|
||||
}, [id]);
|
||||
}, [id, items]);
|
||||
|
||||
function handleCopy(type: string, value: any) {
|
||||
navigator.clipboard.writeText(value);
|
||||
@@ -42,7 +39,13 @@ function ScriptItem() {
|
||||
amountOfScriptsCopied = (parseInt(amountOfScriptsCopied) + 1).toString();
|
||||
localStorage.setItem("amountOfScriptsCopied", amountOfScriptsCopied);
|
||||
|
||||
if (parseInt(amountOfScriptsCopied) === 3 || parseInt(amountOfScriptsCopied) === 10 || parseInt(amountOfScriptsCopied) === 25 || parseInt(amountOfScriptsCopied) === 50 || parseInt(amountOfScriptsCopied) === 100) {
|
||||
if (
|
||||
parseInt(amountOfScriptsCopied) === 3 ||
|
||||
parseInt(amountOfScriptsCopied) === 10 ||
|
||||
parseInt(amountOfScriptsCopied) === 25 ||
|
||||
parseInt(amountOfScriptsCopied) === 50 ||
|
||||
parseInt(amountOfScriptsCopied) === 100
|
||||
) {
|
||||
setTimeout(() => {
|
||||
toast.info(
|
||||
<div className="flex flex-col gap-3">
|
||||
@@ -76,7 +79,6 @@ function ScriptItem() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function closeScript() {
|
||||
// remove the id from the url and reset the state
|
||||
window.history.pushState({}, document.title, window.location.pathname);
|
||||
@@ -384,8 +386,8 @@ function ScriptItem() {
|
||||
)}
|
||||
{item ? null : (
|
||||
<div className="flex w-full flex-col gap-5">
|
||||
<LatestScripts />
|
||||
<MostViewedScripts />
|
||||
<LatestScripts items={items} />
|
||||
<MostViewedScripts items={items} />
|
||||
</div>
|
||||
)}
|
||||
</Suspense>
|
||||
|
||||
@@ -9,15 +9,20 @@ import { Input } from "@/components/ui/input";
|
||||
import Link from "next/link";
|
||||
import React, { useState, useEffect, useRef, useMemo } from "react";
|
||||
import { Category } from "@/lib/types";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import { Badge } from "./ui/badge";
|
||||
import clsx from "clsx";
|
||||
|
||||
const ScriptBrowser = () => {
|
||||
const ScriptBrowser = ({ items }: { items: Category[] }) => {
|
||||
const [links, setLinks] = useState<Category[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (items) {
|
||||
setLinks(items);
|
||||
}
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "/") {
|
||||
@@ -32,24 +37,6 @@ const ScriptBrowser = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const fetchLinks = async () => {
|
||||
try {
|
||||
// you can also fetch all records at once via getFullList
|
||||
const res = await pb.collection("categories").getFullList({
|
||||
expand: "items",
|
||||
sort: "order",
|
||||
requestKey: "desktop",
|
||||
});
|
||||
setLinks(res as unknown as Category[]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching links:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchLinks();
|
||||
}, []);
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchTerm(value);
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ export type Script = {
|
||||
updated: string;
|
||||
website: string;
|
||||
isMostViewed: boolean;
|
||||
mostViewedPosition: number;
|
||||
};
|
||||
|
||||
export interface Category {
|
||||
|
||||
Reference in New Issue
Block a user