Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions src/components/Product/DisplayProducts.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ interface IDisplayProductsProps {
* @param {IDisplayProductsProps} products Products to render
* @returns {JSX.Element} - Rendered component
*/

const DisplayProducts = ({ products }: IDisplayProductsProps) => (
<section className="container mx-auto bg-white py-12">
<div
Expand Down Expand Up @@ -78,11 +77,7 @@ const DisplayProducts = ({ products }: IDisplayProductsProps) => (

return (
<div key={uuidv4()} className="group">
<Link
href={`/produkt/${encodeURIComponent(
slug,
)}?id=${encodeURIComponent(databaseId)}`}
>
<Link href={`/produkt/${encodeURIComponent(slug)}`}>
<div className="aspect-[3/4] relative overflow-hidden bg-gray-100">
{image ? (
<img
Expand All @@ -101,11 +96,7 @@ const DisplayProducts = ({ products }: IDisplayProductsProps) => (
)}
</div>
</Link>
<Link
href={`/produkt/${encodeURIComponent(
slug,
)}?id=${encodeURIComponent(databaseId)}`}
>
<Link href={`/produkt/${encodeURIComponent(slug)}`}>
<span>
<div className="mt-4">
<p className="text-2xl font-bold text-center cursor-pointer hover:text-gray-600 transition-colors">
Expand Down Expand Up @@ -134,7 +125,7 @@ const DisplayProducts = ({ products }: IDisplayProductsProps) => (
</div>
</div>
);
},
}
)
) : (
<div className="mx-auto text-xl font-bold text-center text-gray-800 no-underline uppercase">
Expand Down
21 changes: 15 additions & 6 deletions src/components/Product/ProductCard.component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from 'next/link';
import Image from 'next/image';

import { paddedPrice } from '@/utils/functions/functions';

interface ProductCardProps {
Expand Down Expand Up @@ -27,13 +28,17 @@ const ProductCard = ({
}: ProductCardProps) => {
// Add padding/empty character after currency symbol
const formattedPrice = price ? paddedPrice(price, 'kr') : price;
const formattedRegularPrice = regularPrice ? paddedPrice(regularPrice, 'kr') : regularPrice;
const formattedSalePrice = salePrice ? paddedPrice(salePrice, 'kr') : salePrice;
const formattedRegularPrice = regularPrice
? paddedPrice(regularPrice, 'kr')
: regularPrice;
const formattedSalePrice = salePrice
? paddedPrice(salePrice, 'kr')
: salePrice;

return (
<div className="group">
<div className="aspect-[3/4] overflow-hidden bg-gray-100 relative">
<Link href={`/produkt/${slug}?id=${databaseId}`}>
<Link href={`/produkt/${slug}`}>
{image?.sourceUrl ? (
<Image
src={image.sourceUrl}
Expand All @@ -51,7 +56,7 @@ const ProductCard = ({
</Link>
</div>

<Link href={`/produkt/${slug}?id=${databaseId}`}>
<Link href={`/produkt/${slug}`}>
<div className="mt-4">
<p className="text-2xl font-bold text-center cursor-pointer hover:text-gray-600 transition-colors">
{name}
Expand All @@ -61,8 +66,12 @@ const ProductCard = ({
<div className="mt-2 text-center">
{onSale ? (
<div className="flex items-center justify-center gap-2">
<span className="text-xl font-bold text-red-600">{formattedSalePrice}</span>
<span className="text-lg text-gray-500 line-through">{formattedRegularPrice}</span>
<span className="text-xl font-bold text-red-600">
{formattedSalePrice}
</span>
<span className="text-lg text-gray-500 line-through">
{formattedRegularPrice}
</span>
</div>
) : (
<span className="text-lg text-gray-900">{formattedPrice}</span>
Expand Down
47 changes: 35 additions & 12 deletions src/pages/produkt/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@ import Layout from '@/components/Layout/Layout.component';
import client from '@/utils/apollo/ApolloClient';

// Types
import type {
NextPage,
GetServerSideProps,
InferGetServerSidePropsType,
} from 'next';
import type { NextPage, GetServerSideProps, InferGetServerSidePropsType } from 'next';

// GraphQL
import { GET_SINGLE_PRODUCT } from '@/utils/gql/GQL_QUERIES';

/**
* Display a single product with dynamic pretty urls
* Display a single product with dynamic pretty URLs.
* This implementation removes unnecessary query parameters for SEO purposes,
* and uses the product slug to search for the product. If an "id" is present in the query,
* it redirects to a URL without the id.
* @function Produkt
* @param {InferGetServerSidePropsType<typeof getServerSideProps>} products
* @param {InferGetServerSidePropsType<typeof getServerSideProps>} props
* @returns {JSX.Element} - Rendered component
*/
const Produkt: NextPage = ({
Expand All @@ -30,7 +29,7 @@ const Produkt: NextPage = ({
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
const hasError = networkStatus === '8';
return (
<Layout title={`${product.name ? product.name : ''}`}>
<Layout title={product.name || ''}>
{product ? (
<SingleProduct product={product} />
) : (
Expand All @@ -47,14 +46,38 @@ const Produkt: NextPage = ({

export default withRouter(Produkt);

export const getServerSideProps: GetServerSideProps = async ({
query: { id },
}) => {
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
// Extract 'slug' and 'id' from the query parameters.
const { id, slug } = query;

// For SEO, if an 'id' is provided in the query, redirect to a clean URL using only the slug.
if (id) {
return {
redirect: {
destination: `/produkt/${slug}`,
permanent: true,
},
};
}

// Use the slug to fetch the product via its slug.
const variables = { id: slug, idType: 'SLUG' };

const { data, loading, networkStatus } = await client.query({
query: GET_SINGLE_PRODUCT,
variables: { id },
variables,
});

// If the slug in the URL doesn't match the product's actual slug, redirect to the correct URL.
if (slug && data.product.slug !== slug) {
return {
redirect: {
destination: `/produkt/${data.product.slug}`,
permanent: true,
},
};
}

return {
props: { product: data.product, loading, networkStatus },
};
Expand Down
16 changes: 3 additions & 13 deletions src/utils/gql/GQL_QUERIES.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { gql } from '@apollo/client';

export const GET_SINGLE_PRODUCT = gql`
query Product($id: ID!) {
product(id: $id, idType: DATABASE_ID) {
query Product($id: ID!, $idType: ProductIdTypeEnum!) {
product(id: $id, idType: $idType) {
id
databaseId
averageRating
Expand All @@ -29,6 +29,7 @@ export const GET_SINGLE_PRODUCT = gql`
regularPrice
price
id
stockQuantity
allPaColors {
nodes {
name
Expand Down Expand Up @@ -73,10 +74,6 @@ export const GET_SINGLE_PRODUCT = gql`
}
`;

/**
* Fetch first 4 products from a specific category
*/

export const FETCH_FIRST_PRODUCTS_FROM_HOODIES_QUERY = `
query MyQuery {
products(first: 4, where: {category: "Hoodies"}) {
Expand All @@ -103,9 +100,6 @@ export const FETCH_FIRST_PRODUCTS_FROM_HOODIES_QUERY = `
}
`;

/**
* Fetch first 200 Woocommerce products from GraphQL
*/
export const FETCH_ALL_PRODUCTS_QUERY = gql`
query MyQuery {
products(first: 50) {
Expand Down Expand Up @@ -164,9 +158,6 @@ export const FETCH_ALL_PRODUCTS_QUERY = gql`
}
`;

/**
* Fetch first 20 categories from GraphQL
*/
export const FETCH_ALL_CATEGORIES_QUERY = gql`
query Categories {
productCategories(first: 20) {
Expand Down Expand Up @@ -304,7 +295,6 @@ export const GET_CART = gql`
subtotalTax
}
}

subtotal
subtotalTax
shippingTax
Expand Down
Loading