refactor: Enhance ScriptAccordion and Sidebar components to support selectedCategory state (#7405)

* refactor: Enhance ScriptAccordion and Sidebar components to support selectedCategory state

* lint

* chore: Add ESLint configuration to ignore errors during builds in next.config.mjs
This commit is contained in:
Bram Suurd 2025-09-05 07:24:19 +02:00 committed by GitHub
parent bf05dabc4c
commit 62264f37a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 51 additions and 30 deletions

View File

@ -18,6 +18,10 @@ const nextConfig = {
BASE_PATH: "ProxmoxVE", BASE_PATH: "ProxmoxVE",
}, },
eslint: {
ignoreDuringBuilds: true,
},
output: "export", output: "export",
basePath: `/ProxmoxVE`, basePath: `/ProxmoxVE`,
}; };

View File

@ -73,7 +73,13 @@ function CategoryView() {
}; };
const handleScriptClick = (scriptSlug: string) => { const handleScriptClick = (scriptSlug: string) => {
router.push(`/scripts?id=${scriptSlug}`); // Include category context when navigating to scripts
const categoryName = selectedCategoryIndex !== null ? categories[selectedCategoryIndex]?.name : null;
const queryParams = new URLSearchParams({ id: scriptSlug });
if (categoryName) {
queryParams.append("category", categoryName);
}
router.push(`/scripts?${queryParams.toString()}`);
}; };
const navigateCategory = (direction: "prev" | "next") => { const navigateCategory = (direction: "prev" | "next") => {

View File

@ -4,12 +4,7 @@ import Link from "next/link";
import type { Category } from "@/lib/types"; import type { Category } from "@/lib/types";
import { import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { formattedBadge } from "@/components/command-menu"; import { formattedBadge } from "@/components/command-menu";
import { basePath } from "@/config/site-config"; import { basePath } from "@/config/site-config";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -18,14 +13,16 @@ export default function ScriptAccordion({
items, items,
selectedScript, selectedScript,
setSelectedScript, setSelectedScript,
selectedCategory,
setSelectedCategory,
}: { }: {
items: Category[]; items: Category[];
selectedScript: string | null; selectedScript: string | null;
setSelectedScript: (script: string | null) => void; setSelectedScript: (script: string | null) => void;
selectedCategory: string | null;
setSelectedCategory: (category: string | null) => void;
}) { }) {
const [expandedItem, setExpandedItem] = useState<string | undefined>( const [expandedItem, setExpandedItem] = useState<string | undefined>(undefined);
undefined,
);
const linkRefs = useRef<{ [key: string]: HTMLAnchorElement | null }>({}); const linkRefs = useRef<{ [key: string]: HTMLAnchorElement | null }>({});
const handleAccordionChange = (value: string | undefined) => { const handleAccordionChange = (value: string | undefined) => {
@ -41,15 +38,27 @@ export default function ScriptAccordion({
useEffect(() => { useEffect(() => {
if (selectedScript) { if (selectedScript) {
const category = items.find(category => let category;
category.scripts.some(script => script.slug === selectedScript),
); // If we have a selected category, try to find the script in that specific category
if (selectedCategory) {
category = items.find(
cat => cat.name === selectedCategory && cat.scripts.some(script => script.slug === selectedScript),
);
}
// Fallback: if no category is selected or script not found in selected category,
// use the first category containing the script (backward compatibility)
if (!category) {
category = items.find(category => category.scripts.some(script => script.slug === selectedScript));
}
if (category) { if (category) {
setExpandedItem(category.name); setExpandedItem(category.name);
handleSelected(selectedScript); handleSelected(selectedScript);
} }
} }
}, [selectedScript, items, handleSelected]); }, [selectedScript, selectedCategory, items, handleSelected]);
return ( return (
<Accordion <Accordion
type="single" type="single"
@ -82,10 +91,7 @@ export default function ScriptAccordion({
</div> </div>
{" "} {" "}
</AccordionTrigger> </AccordionTrigger>
<AccordionContent <AccordionContent data-state={expandedItem === category.name ? "open" : "closed"} className="pt-0">
data-state={expandedItem === category.name ? "open" : "closed"}
className="pt-0"
>
{category.scripts {category.scripts
.slice() .slice()
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name))
@ -94,7 +100,7 @@ export default function ScriptAccordion({
<Link <Link
href={{ href={{
pathname: "/scripts", pathname: "/scripts",
query: { id: script.slug }, query: { id: script.slug, category: category.name },
}} }}
prefetch={false} prefetch={false}
className={`flex cursor-pointer items-center justify-between gap-1 px-1 py-1 text-muted-foreground hover:rounded-lg hover:bg-accent/60 hover:dark:bg-accent/20 ${ className={`flex cursor-pointer items-center justify-between gap-1 px-1 py-1 text-muted-foreground hover:rounded-lg hover:bg-accent/60 hover:dark:bg-accent/20 ${
@ -102,7 +108,10 @@ export default function ScriptAccordion({
? "rounded-lg bg-accent font-semibold dark:bg-accent/30 dark:text-white" ? "rounded-lg bg-accent font-semibold dark:bg-accent/30 dark:text-white"
: "" : ""
}`} }`}
onClick={() => handleSelected(script.slug)} onClick={() => {
handleSelected(script.slug);
setSelectedCategory(category.name);
}}
ref={(el) => { ref={(el) => {
linkRefs.current[script.slug] = el; linkRefs.current[script.slug] = el;
}} }}
@ -113,15 +122,11 @@ export default function ScriptAccordion({
height={16} height={16}
width={16} width={16}
unoptimized unoptimized
onError={e => onError={e => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
((e.currentTarget as HTMLImageElement).src
= `/${basePath}/logo.png`)}
alt={script.name} alt={script.name}
className="mr-1 w-4 h-4 rounded-full" className="mr-1 w-4 h-4 rounded-full"
/> />
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">{script.name}</span>
{script.name}
</span>
</div> </div>
{formattedBadge(script.type)} {formattedBadge(script.type)}
</Link> </Link>

View File

@ -8,10 +8,14 @@ function Sidebar({
items, items,
selectedScript, selectedScript,
setSelectedScript, setSelectedScript,
selectedCategory,
setSelectedCategory,
}: { }: {
items: Category[]; items: Category[];
selectedScript: string | null; selectedScript: string | null;
setSelectedScript: (script: string | null) => void; setSelectedScript: (script: string | null) => void;
selectedCategory: string | null;
setSelectedCategory: (category: string | null) => void;
}) { }) {
const uniqueScripts = items.reduce((acc, category) => { const uniqueScripts = items.reduce((acc, category) => {
for (const script of category.scripts) { for (const script of category.scripts) {
@ -37,6 +41,8 @@ function Sidebar({
items={items} items={items}
selectedScript={selectedScript} selectedScript={selectedScript}
setSelectedScript={setSelectedScript} setSelectedScript={setSelectedScript}
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
/> />
</div> </div>
</div> </div>

View File

@ -8,16 +8,14 @@ import type { Category, Script } from "@/lib/types";
import { ScriptItem } from "@/app/scripts/_components/script-item"; import { ScriptItem } from "@/app/scripts/_components/script-item";
import { fetchCategories } from "@/lib/data"; import { fetchCategories } from "@/lib/data";
import { import { LatestScripts, MostViewedScripts } from "./_components/script-info-blocks";
LatestScripts,
MostViewedScripts,
} from "./_components/script-info-blocks";
import Sidebar from "./_components/sidebar"; import Sidebar from "./_components/sidebar";
export const dynamic = "force-static"; export const dynamic = "force-static";
function ScriptContent() { function ScriptContent() {
const [selectedScript, setSelectedScript] = useQueryState("id"); const [selectedScript, setSelectedScript] = useQueryState("id");
const [selectedCategory, setSelectedCategory] = useQueryState("category");
const [links, setLinks] = useState<Category[]>([]); const [links, setLinks] = useState<Category[]>([]);
const [item, setItem] = useState<Script>(); const [item, setItem] = useState<Script>();
@ -47,6 +45,8 @@ function ScriptContent() {
items={links} items={links}
selectedScript={selectedScript} selectedScript={selectedScript}
setSelectedScript={setSelectedScript} setSelectedScript={setSelectedScript}
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
/> />
</div> </div>
<div className="mx-4 w-full sm:mx-0 sm:ml-4"> <div className="mx-4 w-full sm:mx-0 sm:ml-4">