diff --git a/src/components/Product/DisplayProducts.component.tsx b/src/components/Product/DisplayProducts.component.tsx index d6b75eef1..6f8fe2ab1 100644 --- a/src/components/Product/DisplayProducts.component.tsx +++ b/src/components/Product/DisplayProducts.component.tsx @@ -45,7 +45,6 @@ interface IDisplayProductsProps { * @param {IDisplayProductsProps} products Products to render * @returns {JSX.Element} - Rendered component */ - const DisplayProducts = ({ products }: IDisplayProductsProps) => (
( return (
- +
{image ? ( ( )}
- +

@@ -134,7 +125,7 @@ const DisplayProducts = ({ products }: IDisplayProductsProps) => (

); - }, + } ) ) : (
diff --git a/src/components/Product/ProductCard.component.tsx b/src/components/Product/ProductCard.component.tsx index 77f535b57..a3c23e7fb 100644 --- a/src/components/Product/ProductCard.component.tsx +++ b/src/components/Product/ProductCard.component.tsx @@ -1,5 +1,6 @@ import Link from 'next/link'; import Image from 'next/image'; + import { paddedPrice } from '@/utils/functions/functions'; interface ProductCardProps { @@ -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 (
- + {image?.sourceUrl ? (
- +

{name} @@ -61,8 +66,12 @@ const ProductCard = ({

{onSale ? (
- {formattedSalePrice} - {formattedRegularPrice} + + {formattedSalePrice} + + + {formattedRegularPrice} +
) : ( {formattedPrice} diff --git a/src/pages/produkt/[slug].tsx b/src/pages/produkt/[slug].tsx index 711da637c..d1971373f 100644 --- a/src/pages/produkt/[slug].tsx +++ b/src/pages/produkt/[slug].tsx @@ -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} products + * @param {InferGetServerSidePropsType} props * @returns {JSX.Element} - Rendered component */ const Produkt: NextPage = ({ @@ -30,7 +29,7 @@ const Produkt: NextPage = ({ }: InferGetServerSidePropsType) => { const hasError = networkStatus === '8'; return ( - + {product ? ( ) : ( @@ -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 }, }; diff --git a/src/utils/gql/GQL_QUERIES.ts b/src/utils/gql/GQL_QUERIES.ts index b3edca9dd..9b9996eed 100644 --- a/src/utils/gql/GQL_QUERIES.ts +++ b/src/utils/gql/GQL_QUERIES.ts @@ -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 @@ -29,6 +29,7 @@ export const GET_SINGLE_PRODUCT = gql` regularPrice price id + stockQuantity allPaColors { nodes { name @@ -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"}) { @@ -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) { @@ -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) { @@ -304,7 +295,6 @@ export const GET_CART = gql` subtotalTax } } - subtotal subtotalTax shippingTax