mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 02:12:49 +00:00 
			
		
		
		
	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:
		@@ -18,6 +18,10 @@ const nextConfig = {
 | 
			
		||||
    BASE_PATH: "ProxmoxVE",
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  eslint: {
 | 
			
		||||
    ignoreDuringBuilds: true,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  output: "export",
 | 
			
		||||
  basePath: `/ProxmoxVE`,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,13 @@ function CategoryView() {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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") => {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,7 @@ import Link from "next/link";
 | 
			
		||||
 | 
			
		||||
import type { Category } from "@/lib/types";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  Accordion,
 | 
			
		||||
  AccordionContent,
 | 
			
		||||
  AccordionItem,
 | 
			
		||||
  AccordionTrigger,
 | 
			
		||||
} from "@/components/ui/accordion";
 | 
			
		||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
 | 
			
		||||
import { formattedBadge } from "@/components/command-menu";
 | 
			
		||||
import { basePath } from "@/config/site-config";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
@@ -18,14 +13,16 @@ export default function ScriptAccordion({
 | 
			
		||||
  items,
 | 
			
		||||
  selectedScript,
 | 
			
		||||
  setSelectedScript,
 | 
			
		||||
  selectedCategory,
 | 
			
		||||
  setSelectedCategory,
 | 
			
		||||
}: {
 | 
			
		||||
  items: Category[];
 | 
			
		||||
  selectedScript: string | null;
 | 
			
		||||
  setSelectedScript: (script: string | null) => void;
 | 
			
		||||
  selectedCategory: string | null;
 | 
			
		||||
  setSelectedCategory: (category: string | null) => void;
 | 
			
		||||
}) {
 | 
			
		||||
  const [expandedItem, setExpandedItem] = useState<string | undefined>(
 | 
			
		||||
    undefined,
 | 
			
		||||
  );
 | 
			
		||||
  const [expandedItem, setExpandedItem] = useState<string | undefined>(undefined);
 | 
			
		||||
  const linkRefs = useRef<{ [key: string]: HTMLAnchorElement | null }>({});
 | 
			
		||||
 | 
			
		||||
  const handleAccordionChange = (value: string | undefined) => {
 | 
			
		||||
@@ -41,15 +38,27 @@ export default function ScriptAccordion({
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (selectedScript) {
 | 
			
		||||
      const category = items.find(category =>
 | 
			
		||||
        category.scripts.some(script => script.slug === selectedScript),
 | 
			
		||||
      );
 | 
			
		||||
      let category;
 | 
			
		||||
 | 
			
		||||
      // 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) {
 | 
			
		||||
        setExpandedItem(category.name);
 | 
			
		||||
        handleSelected(selectedScript);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }, [selectedScript, items, handleSelected]);
 | 
			
		||||
  }, [selectedScript, selectedCategory, items, handleSelected]);
 | 
			
		||||
  return (
 | 
			
		||||
    <Accordion
 | 
			
		||||
      type="single"
 | 
			
		||||
@@ -82,10 +91,7 @@ export default function ScriptAccordion({
 | 
			
		||||
            </div>
 | 
			
		||||
            {" "}
 | 
			
		||||
          </AccordionTrigger>
 | 
			
		||||
          <AccordionContent
 | 
			
		||||
            data-state={expandedItem === category.name ? "open" : "closed"}
 | 
			
		||||
            className="pt-0"
 | 
			
		||||
          >
 | 
			
		||||
          <AccordionContent data-state={expandedItem === category.name ? "open" : "closed"} className="pt-0">
 | 
			
		||||
            {category.scripts
 | 
			
		||||
              .slice()
 | 
			
		||||
              .sort((a, b) => a.name.localeCompare(b.name))
 | 
			
		||||
@@ -94,7 +100,7 @@ export default function ScriptAccordion({
 | 
			
		||||
                  <Link
 | 
			
		||||
                    href={{
 | 
			
		||||
                      pathname: "/scripts",
 | 
			
		||||
                      query: { id: script.slug },
 | 
			
		||||
                      query: { id: script.slug, category: category.name },
 | 
			
		||||
                    }}
 | 
			
		||||
                    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 ${
 | 
			
		||||
@@ -102,7 +108,10 @@ export default function ScriptAccordion({
 | 
			
		||||
                        ? "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) => {
 | 
			
		||||
                      linkRefs.current[script.slug] = el;
 | 
			
		||||
                    }}
 | 
			
		||||
@@ -113,15 +122,11 @@ export default function ScriptAccordion({
 | 
			
		||||
                        height={16}
 | 
			
		||||
                        width={16}
 | 
			
		||||
                        unoptimized
 | 
			
		||||
                        onError={e =>
 | 
			
		||||
                          ((e.currentTarget as HTMLImageElement).src
 | 
			
		||||
                            = `/${basePath}/logo.png`)}
 | 
			
		||||
                        onError={e => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
 | 
			
		||||
                        alt={script.name}
 | 
			
		||||
                        className="mr-1 w-4 h-4 rounded-full"
 | 
			
		||||
                      />
 | 
			
		||||
                      <span className="flex items-center gap-2">
 | 
			
		||||
                        {script.name}
 | 
			
		||||
                      </span>
 | 
			
		||||
                      <span className="flex items-center gap-2">{script.name}</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {formattedBadge(script.type)}
 | 
			
		||||
                  </Link>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,14 @@ function Sidebar({
 | 
			
		||||
  items,
 | 
			
		||||
  selectedScript,
 | 
			
		||||
  setSelectedScript,
 | 
			
		||||
  selectedCategory,
 | 
			
		||||
  setSelectedCategory,
 | 
			
		||||
}: {
 | 
			
		||||
  items: Category[];
 | 
			
		||||
  selectedScript: string | null;
 | 
			
		||||
  setSelectedScript: (script: string | null) => void;
 | 
			
		||||
  selectedCategory: string | null;
 | 
			
		||||
  setSelectedCategory: (category: string | null) => void;
 | 
			
		||||
}) {
 | 
			
		||||
  const uniqueScripts = items.reduce((acc, category) => {
 | 
			
		||||
    for (const script of category.scripts) {
 | 
			
		||||
@@ -37,6 +41,8 @@ function Sidebar({
 | 
			
		||||
          items={items}
 | 
			
		||||
          selectedScript={selectedScript}
 | 
			
		||||
          setSelectedScript={setSelectedScript}
 | 
			
		||||
          selectedCategory={selectedCategory}
 | 
			
		||||
          setSelectedCategory={setSelectedCategory}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,16 +8,14 @@ import type { Category, Script } from "@/lib/types";
 | 
			
		||||
import { ScriptItem } from "@/app/scripts/_components/script-item";
 | 
			
		||||
import { fetchCategories } from "@/lib/data";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  LatestScripts,
 | 
			
		||||
  MostViewedScripts,
 | 
			
		||||
} from "./_components/script-info-blocks";
 | 
			
		||||
import { LatestScripts, MostViewedScripts } from "./_components/script-info-blocks";
 | 
			
		||||
import Sidebar from "./_components/sidebar";
 | 
			
		||||
 | 
			
		||||
export const dynamic = "force-static";
 | 
			
		||||
 | 
			
		||||
function ScriptContent() {
 | 
			
		||||
  const [selectedScript, setSelectedScript] = useQueryState("id");
 | 
			
		||||
  const [selectedCategory, setSelectedCategory] = useQueryState("category");
 | 
			
		||||
  const [links, setLinks] = useState<Category[]>([]);
 | 
			
		||||
  const [item, setItem] = useState<Script>();
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +45,8 @@ function ScriptContent() {
 | 
			
		||||
            items={links}
 | 
			
		||||
            selectedScript={selectedScript}
 | 
			
		||||
            setSelectedScript={setSelectedScript}
 | 
			
		||||
            selectedCategory={selectedCategory}
 | 
			
		||||
            setSelectedCategory={setSelectedCategory}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="mx-4 w-full sm:mx-0 sm:ml-4">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user