Untitled

 avatar
unknown
javascript
8 months ago
7.3 kB
4
Indexable
// route.tsx
export async function loader({ params, context, request }: LoaderArgs) {
  const { handle } = params;
  const url = new URL(request.url);
  let category: any = [];

  try {
    const [reqCollection, reqVideo] = await Promise.all([
      fetchAPI('/api/shopify/custom-menu-metaobject', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          first: 26,
          type: 'custom_menu',
          handle,
          language: params.language ? params.language.toUpperCase() : 'IT',
          country: 'IT',
        }),
      }, context, request),

      fetchAPI('/api/shopify/metaobjects-new', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          first: 26,
          type: 'custom_menu',
          language: params.language ? params.language.toUpperCase() : 'IT',
          country: 'IT',
        }),
      }, context, request),
    ]);

    if (reqVideo.status == 200) {
      const metaobject: MetaobjectResponse = await reqVideo.json() as MetaobjectResponse;

      const categoryData = metaobject.metaobjects.edges.find(
        (el) => el.node.handle === handle,
      );
      category = categoryData?.node || [];
    } else {
      throw new Response(null, { status: 404, statusText: 'Not Found' });
    }

    if (reqCollection.status == 200) {
      const check: any = await reqCollection.json();
      if (check) {
        const categoryWithCollections = {
          ...category,
          fields: [
            ...category.fields.filter((el: any) => el.key !== 'collection'),
            { ...check?.fields?.find((item: any) => item.key === 'collection') },
          ],
        };
        category = categoryWithCollections;
      }
    } else {
      throw new Error(`Error fetching collection: ${reqCollection.statusText}`);
    }
  } catch (error) {
    console.error(error);
    throw new Error('E101: Errore nel recuperare le informazioni.');
  }

  return defer({ category });
}

const ListingPage = () => {
  const {category} = useLoaderData<typeof loader>();

  const valuesRef = useRef<null | {
    memoizedFields: MemoizedFields;
    products: TProductInfo[];
    hasVideo: boolean;
  }>(null);
  
  if (valuesRef.current === null) {
    const getField = (key: string) => category.fields.find((e: any) => e.key === key);
    
    const fields = {
      titolo_menu: getField('titolo_menu'),
      video: getField('video'),
      video_mobile: getField('video_mobile'),
      titolo_pagina: getField('titolo'),
      titolo_lista: getField('titolo_lista'),
      descrizione_pagina: getField('descrizione'),
      collection: getField('collection'),
    };
  
    valuesRef.current = {
      memoizedFields: fields,
      products: fields.collection?.reference?.products?.nodes || [],
      hasVideo: Boolean(fields.video?.reference?.sources?.[0]?.url)
    };
  }
  
  const { memoizedFields, products, hasVideo } = valuesRef.current;

  useSendGAEvent(EventNames.ViewItemList, {
    item_list_id: memoizedFields.titolo_lista?.value ?? 'custom menu products',
    items: products,
  });

  return (
    <div className="w-full bg-background" data-element="preferiti-di-cristina-listing-page">
      <div className="container-site mx-auto relative">
        {memoizedFields.titolo_pagina && <PageHeaderListingPage memoizedFields={memoizedFields} hasVideo={hasVideo} category={category} />}
        <ProductListListingPage products={products} titolo_lista={memoizedFields.titolo_lista} />
      </div>
    </div>
  );
};

export default ListingPage;


// PageHeader.tsx
interface PageHeaderProps {
  memoizedFields: MemoizedFields;
  hasVideo: boolean;
  category: { handle: string };
}

const PageHeaderListingPage: React.FC<PageHeaderProps> = React.memo(({ memoizedFields, hasVideo, category }) => (
  <div className="w-full pb-10" data-element="preferiti-di-cristina-page-header">
    <Breadcrumb
      items={[
        {
          label: memoizedFields.titolo_menu?.value ?? '',
          url: `/custom/${category.handle}`,
        },
      ]}
    />
    <div className="flex flex-col md:container md:mx-auto">
      {memoizedFields.titolo_menu && (
        <span className="md:hidden text-[14px] leading-[18px] font-bold text-rhodamine uppercase">
          {memoizedFields.titolo_menu.value}
        </span>
      )}
      
      <div className="flex flex-col md:flex-row items-center">
        <ContentListingPage
          memoizedFields={memoizedFields}
          hasVideo={hasVideo}
          className="order-2 md:order-1 flex-1 mb-4 md:mb-0"
        />
        {hasVideo && memoizedFields.video && (
            <VideoSectionListingPage
              video={memoizedFields.video}
              className="order-1 md:order-2 flex-1 my-3 md:my-0"
            />
        )}
      </div>
    </div>

  </div>
));

export default PageHeaderListingPage;


// Content.tsx
interface ContentProps {
  memoizedFields: MemoizedFields;
  hasVideo: boolean;
  className?: string;
}

const ContentListingPage: React.FC<ContentProps> = React.memo(({ memoizedFields, hasVideo, className }) => (
  <div 
    className={`flex flex-col gap-2 font-light pb-6 ${hasVideo ? 'md:max-w-[445px]' : ''} ${className}`} 
    data-element="preferiti-di-cristina-content"
  >
    {memoizedFields.titolo_menu && (
      <span className="hidden md:block text-[14px] leading-[18px] font-bold text-rhodamine uppercase">
        {memoizedFields.titolo_menu.value}
      </span>
    )}
    {memoizedFields.titolo_pagina && <h3 className="bold">{memoizedFields.titolo_pagina.value}</h3>}
    {memoizedFields.descrizione_pagina && (
      <div
        className="text-left"
        dangerouslySetInnerHTML={{
          __html: toHTML(memoizedFields.descrizione_pagina.value),
        }}
      ></div>
    )}
  </div>
));

export default ContentListingPage;


// VideoSection.tsx
interface VideoSectionProps {
  video: { reference: { sources: { url: string }[] } };
  className?: string;
}

const VideoSectionListingPage: React.FC<VideoSectionProps> = React.memo(({ video, className }) => (
  <div 
    className={`flex items-center justify-center mx-auto w-[320px] xxxs:w-[359px] md:w-[670px] ${className}`} 
    data-element="preferiti-di-cristina-video-section"
  >
    <ProductVideo
      enabled={true}
      video={video.reference.sources[0].url}
    />
  </div>
));

export default VideoSectionListingPage;


// ProductList
interface ProductListProps {
  products: TProductInfo[];
  titolo_lista?: { value: string };
}

const ProductListListingPage: React.FC<ProductListProps> = React.memo(({ products, titolo_lista }) => (
  <div className="container-account mx-auto" data-element="preferiti-di-cristina-product-list">
    <div className="flex flex-col md:flex-row justify-between mt-6 mb-[120px]">
      {products.length > 0 && (
        <div>
          {titolo_lista && <h4 className="mb-6">{titolo_lista.value}</h4>}
          <div className="grid grid-cols-2 md:grid-cols-3 2xl:grid-cols-4 gap-4 justify-center items-center">
            {products.map((product: TProductInfo) => (
              <ProductCardNew key={product.id} product={product} />
            ))}
          </div>
        </div>
      )}
    </div>
  </div>
));

export default ProductListListingPage;
Editor is loading...
Leave a Comment