Commit 7f97455d authored by Labros Papadopoulos's avatar Labros Papadopoulos
Browse files

fix: click specification card

parent 736413e2
Loading
Loading
Loading
Loading
Loading
+53 −15
Original line number Diff line number Diff line
@@ -25,19 +25,43 @@ export default function NavigationTree({
  const treeRef = useRef<HTMLDivElement>(null);

  const [isOpen, setIsOpen] = useState(false);
  const [selectedPath, setSelectedPath] = useState<string | undefined>(undefined);

  //   const [selectedPath, setSelectedPath] = useState<string | undefined>(undefined);
  const selectedPath = searchParams?.get('categoryPath') || undefined;
  useEffect(() => {
    if (level == 0) {
    if (level === 0) {
      setIsOpen(true);
    } else if (selectedPath && hierarchy?.children?.some((child) => child.path === selectedPath)) {
      setIsOpen(true);
    }
  }, [level]);
  }, [level, selectedPath, hierarchy?.children]);

  useEffect(() => {
    if (level !== 0) return; // Attach listener only at root tree level

    const handleClickOutside = (event: MouseEvent) => {
      if (treeRef.current && !treeRef.current.contains(event.target as Node)) {
        setSelectedPath(undefined);
        router.replace(pathname || '');
      const target = event.target as HTMLElement;

      // Ignore if click is inside the tree itself
      if (treeRef.current && treeRef.current.contains(target)) {
        return;
      }

      // Ignore if click is on a spec card, modal, search input, or sorting dropdown
      if (
        target.closest('.group\\/item') || // Spec card
        target.closest('[role="dialog"]') || // Modal detail view
        target.closest('input') || // Search box
        target.closest('button') // Sorting / load buttons
      ) {
        return;
      }

      // Clear category filter when clicking empty background space
      const params = new URLSearchParams(searchParams?.toString() || '');
      if (params.has('categoryPath')) {
        params.delete('categoryPath');
        const queryString = params.toString();
        router.replace(queryString ? `${pathname}?${queryString}` : pathname);
      }
    };

@@ -45,18 +69,26 @@ export default function NavigationTree({
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [treeRef]);
  }, [level, pathname, searchParams, router]);

  const onSelected = (path: string | undefined) => {
    const params = new URLSearchParams(searchParams?.toString() || '');
    params.set('searchText', '');
    params.set('categoryPath', path || '');
    router.replace(pathname + '?' + params.toString());
    setSelectedPath(path);

    if (path) {
      params.set('categoryPath', path);
    } else {
      params.delete('categoryPath');
    }

    // Retains all other search params (like searchText, status, etc.)
    const queryString = params.toString();
    const newUrl = queryString ? `${pathname}?${queryString}` : pathname;

    router.replace(newUrl);
  };

  const isCurrentPath = (path: string | undefined) => {
    return selectedPath === path;
    return Boolean(path && selectedPath === path);
  };

  const levelPaddings = ['pl-0', 'pl-0', 'pl-6', 'pl-10'];
@@ -74,13 +106,19 @@ export default function NavigationTree({
                <IoMdArrowDropdown
                  className="min-w-4"
                  color="#98A2B3"
                  onClick={() => setIsOpen(!isOpen)}
                  onClick={(e) => {
                    e.stopPropagation();
                    setIsOpen(!isOpen);
                  }}
                />
              ) : (
                <IoMdArrowDropright
                  className="min-w-4"
                  color="#98A2B3"
                  onClick={() => setIsOpen(!isOpen)}
                  onClick={(e) => {
                    e.stopPropagation();
                    setIsOpen(!isOpen);
                  }}
                />
              )
            ) : (
+7 −3
Original line number Diff line number Diff line
@@ -26,11 +26,15 @@ export function SearchBox({

  useEffect(() => {
    const params = new URLSearchParams(searchParams?.toString() || '');

    if (debouncedValue) {
      params.set('categoryPath', '');
    }
      params.set('searchText', debouncedValue);
    router.replace(pathname + '?' + params.toString());
    } else {
      params.delete('searchText');
    }

    const queryString = params.toString();
    router.replace(queryString ? `${pathname}?${queryString}` : pathname);
  }, [debouncedValue]);

  return (
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ export default function ServiceInfoComponent({
    ? new Date(serviceSpecificationDetails.lastUpdate)
    : new Date();

  const formattedDate = new Intl.DateTimeFormat(undefined, {
  const formattedDate = new Intl.DateTimeFormat('en-US', {
    day: 'numeric',
    month: 'short',
    year: 'numeric',
+63 −72
Original line number Diff line number Diff line
'use client';

import { addCartItem } from '@/services/cart-api';
import { ServiceSpecification } from '@/services/tmf633';
import { Button, Image, Tooltip } from '@nextui-org/react';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
// import { revalidatePath } from 'next/cache';
import { useSearchParams } from 'next/navigation';
import React from 'react';
import { FaCartPlus } from 'react-icons/fa';
import ServiceInfoComponent from './service-info-component';
import Link from 'next/link';

export function ServiceSpecificationCard({
  userId,
  currentPath,
  serviceSpecification,
}: Readonly<{ userId: string; currentPath: string; serviceSpecification: ServiceSpecification }>) {
  const searchParams = useSearchParams();
  const isOffline =
    (serviceSpecification as ServiceSpecification & { domainGatewayStatus?: string })
      ?.domainGatewayStatus === 'Offline';
  const [prefix, ...suffixParts] = (serviceSpecification.name || '').split(':');
  const suffix = suffixParts.join(':').trim();
  const params = new URLSearchParams(searchParams?.toString() || '');
  params.set('specId', serviceSpecification?.id || '');
  const targetUrl = `/marketplace?${params.toString()}`;

  return (
    <div className="group/item relative w-full max-w-[384px] justify-center">
      <form
        action={async () => {
          'use server';
          const searchParams = new URLSearchParams(global.window?.location.search || '');
          searchParams.set('specId', serviceSpecification?.id || '');
          const newPath = `/marketplace?${searchParams.toString()}`;

          redirect(newPath);
        }}
      <Link
        href={targetUrl}
        className="flex w-full flex-col rounded-sm bg-white text-left text-maestro-black shadow-md"
      >
        <button className="flex w-full flex-col rounded-sm bg-white text-maestro-black shadow-md">
        <div
          className={`flex h-[163px] w-full flex-col justify-between gap-7 px-[30px] ${
            isOffline
@@ -59,9 +59,7 @@ export function ServiceSpecificationCard({
          className={`flex h-[163px] w-full justify-between gap-3.5 p-[30px] ${isOffline ? 'opacity-[0.85]' : ''}`}
        >
          <div className="flex flex-col items-start gap-2">
              <h3 className="text-[11px] font-light leading-tight text-maestro-black">
                DESCRIPTION
              </h3>
            <h3 className="text-[11px] font-light leading-tight text-maestro-black">DESCRIPTION</h3>
            <div className="text-left text-sm font-light leading-5 text-maestro-grey">
              <Tooltip
                className="max-w-64"
@@ -84,24 +82,17 @@ export function ServiceSpecificationCard({
            <Image src={serviceSpecification.attachment?.[0]?.url} alt="logo" width={55} />
          </div>
        </div>
        </button>
      </form>

      <form
        action={async () => {
          'use server';
      </Link>

          await addCartItem(userId, serviceSpecification);

          revalidatePath(currentPath);
        }}
      >
      <Button
        className={`invisible absolute right-9 top-10 h-8 min-w-8 rounded bg-[#4AA5AF] px-0 ${isOffline ? '' : 'group-hover/item:visible'}`}
          type="submit"
        onClick={async (e) => {
          e.preventDefault();
          e.stopPropagation();
          await addCartItem(userId, serviceSpecification);
        }}
        startContent={<FaCartPlus size="1.125rem" />}
      ></Button>
      </form>
    </div>
  );
}
+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ export default function ServiceSpecificationDetailCard({
    const pathname = url.pathname;
    const searchParams = url.searchParams;
    searchParams.delete('specId');
    const newPath = pathname + '?' + searchParams.toString();
    const newPath = searchParams.toString() ? `${pathname}?${searchParams.toString()}` : pathname;
    redirect(newPath);
  };