Skip to content

Commit c287d34

Browse files
committed
Test new product page
1 parent 3d0c668 commit c287d34

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import Link from 'next/link';
2+
import Image from 'next/image';
3+
import { useState } from 'react';
4+
5+
interface ProductCardProps {
6+
databaseId: number;
7+
name: string;
8+
price: string;
9+
slug: string;
10+
image?: {
11+
sourceUrl?: string;
12+
};
13+
}
14+
15+
const ProductCard = ({ databaseId, name, price, slug, image }: ProductCardProps) => {
16+
const [isFavorite, setIsFavorite] = useState(false);
17+
18+
return (
19+
<div className="group">
20+
<div className="aspect-[3/4] relative overflow-hidden bg-gray-100">
21+
<button
22+
onClick={() => setIsFavorite(!isFavorite)}
23+
className="absolute right-4 top-4 z-10 rounded-full bg-white p-2 hover:scale-110 transition-transform duration-200"
24+
>
25+
<svg
26+
className={`h-5 w-5 ${
27+
isFavorite ? 'fill-red-500' : 'fill-none stroke-gray-600'
28+
}`}
29+
viewBox="0 0 24 24"
30+
stroke="currentColor"
31+
strokeWidth="2"
32+
>
33+
<path
34+
strokeLinecap="round"
35+
strokeLinejoin="round"
36+
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
37+
/>
38+
</svg>
39+
</button>
40+
41+
<Link href={`/produkt/${slug}?id=${databaseId}`}>
42+
{image?.sourceUrl ? (
43+
<Image
44+
src={image.sourceUrl}
45+
alt={name}
46+
fill
47+
className="w-full h-full object-cover object-center transition duration-300 group-hover:scale-105"
48+
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
49+
/>
50+
) : (
51+
<div className="h-full w-full bg-gray-100 flex items-center justify-center">
52+
<span className="text-gray-400">No image</span>
53+
</div>
54+
)}
55+
</Link>
56+
</div>
57+
58+
<Link href={`/produkt/${slug}?id=${databaseId}`}>
59+
<div className="mt-4">
60+
<p className="text-base font-bold text-center cursor-pointer hover:text-gray-600 transition-colors">
61+
{name}
62+
</p>
63+
</div>
64+
</Link>
65+
<div className="mt-2 text-center">
66+
<span className="text-gray-900">{price}</span>
67+
</div>
68+
</div>
69+
);
70+
};
71+
72+
export default ProductCard;

src/pages/products2.tsx

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { useState } from 'react';
2+
import Head from 'next/head';
3+
import Layout from '@/components/Layout/Layout.component';
4+
import ProductCard from '@/components/Product/ProductCard.component';
5+
import client from '@/utils/apollo/ApolloClient';
6+
import { FETCH_ALL_PRODUCTS_QUERY } from '@/utils/gql/GQL_QUERIES';
7+
import type { NextPage, GetStaticProps, InferGetStaticPropsType } from 'next';
8+
9+
interface Image {
10+
__typename: string;
11+
sourceUrl?: string;
12+
}
13+
14+
interface Node {
15+
__typename: string;
16+
price: string;
17+
regularPrice: string;
18+
salePrice?: string;
19+
}
20+
21+
interface Variations {
22+
__typename: string;
23+
nodes: Node[];
24+
}
25+
26+
interface Product {
27+
__typename: string;
28+
databaseId: number;
29+
name: string;
30+
onSale: boolean;
31+
slug: string;
32+
image: Image;
33+
price: string;
34+
regularPrice: string;
35+
salePrice?: string;
36+
variations: Variations;
37+
}
38+
39+
const Products2: NextPage = ({
40+
products,
41+
loading,
42+
}: InferGetStaticPropsType<typeof getStaticProps>) => {
43+
const [sortBy, setSortBy] = useState('popular');
44+
const [selectedSizes, setSelectedSizes] = useState<string[]>([]);
45+
const [selectedColors, setSelectedColors] = useState<string[]>([]);
46+
const [priceRange, setPriceRange] = useState<[number, number]>([0, 1000]);
47+
48+
if (loading) return (
49+
<Layout title="Produkter">
50+
<div className="flex justify-center items-center min-h-screen">
51+
<div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-gray-900"></div>
52+
</div>
53+
</Layout>
54+
);
55+
56+
if (!products) return (
57+
<Layout title="Produkter">
58+
<div className="flex justify-center items-center min-h-screen">
59+
<p className="text-red-500">Ingen produkter funnet</p>
60+
</div>
61+
</Layout>
62+
);
63+
64+
const sizes = ['XS', 'S', 'M', 'L', 'XL', 'XXL'];
65+
const colors = [
66+
{ name: 'Svart', class: 'bg-black' },
67+
{ name: 'Brun', class: 'bg-brown-500' },
68+
{ name: 'Beige', class: 'bg-[#D2B48C]' },
69+
{ name: 'Grå', class: 'bg-gray-500' },
70+
{ name: 'Hvit', class: 'bg-white border border-gray-300' },
71+
{ name: 'Blå', class: 'bg-blue-500' }
72+
];
73+
74+
const toggleSize = (size: string) => {
75+
setSelectedSizes(prev =>
76+
prev.includes(size)
77+
? prev.filter(s => s !== size)
78+
: [...prev, size]
79+
);
80+
};
81+
82+
const toggleColor = (color: string) => {
83+
setSelectedColors(prev =>
84+
prev.includes(color)
85+
? prev.filter(c => c !== color)
86+
: [...prev, color]
87+
);
88+
};
89+
90+
return (
91+
<Layout title="Produkter">
92+
<Head>
93+
<title>Produkter | WooCommerce Next.js</title>
94+
</Head>
95+
96+
<div className="container mx-auto px-4 py-8">
97+
<div className="flex flex-col md:flex-row gap-8">
98+
{/* Sidebar Filters */}
99+
<div className="w-full md:w-64 flex-shrink-0">
100+
<div className="bg-white p-6 rounded-lg shadow-sm">
101+
<div className="mb-8">
102+
<h3 className="font-semibold mb-4">PRODUKT TYPE</h3>
103+
<div className="space-y-2">
104+
<label className="flex items-center">
105+
<input type="checkbox" className="form-checkbox" />
106+
<span className="ml-2">T-Shirts</span>
107+
</label>
108+
<label className="flex items-center">
109+
<input type="checkbox" className="form-checkbox" />
110+
<span className="ml-2">Gensere</span>
111+
</label>
112+
<label className="flex items-center">
113+
<input type="checkbox" className="form-checkbox" />
114+
<span className="ml-2">Singlet</span>
115+
</label>
116+
<label className="flex items-center">
117+
<input type="checkbox" className="form-checkbox" />
118+
<span className="ml-2">Skjorter</span>
119+
</label>
120+
</div>
121+
</div>
122+
123+
<div className="mb-8">
124+
<h3 className="font-semibold mb-4">PRIS</h3>
125+
<input
126+
type="range"
127+
min="0"
128+
max="1000"
129+
value={priceRange[1]}
130+
onChange={(e) => setPriceRange([priceRange[0], parseInt(e.target.value)])}
131+
className="w-full"
132+
/>
133+
<div className="flex justify-between mt-2">
134+
<span>kr {priceRange[0]}</span>
135+
<span>kr {priceRange[1]}</span>
136+
</div>
137+
</div>
138+
139+
<div className="mb-8">
140+
<h3 className="font-semibold mb-4">STØRRELSE</h3>
141+
<div className="grid grid-cols-3 gap-2">
142+
{sizes.map((size) => (
143+
<button
144+
key={size}
145+
onClick={() => toggleSize(size)}
146+
className={`px-3 py-1 border rounded ${
147+
selectedSizes.includes(size)
148+
? 'bg-gray-900 text-white'
149+
: 'hover:bg-gray-100'
150+
}`}
151+
>
152+
{size}
153+
</button>
154+
))}
155+
</div>
156+
</div>
157+
158+
<div>
159+
<h3 className="font-semibold mb-4">FARGE</h3>
160+
<div className="grid grid-cols-3 gap-2">
161+
{colors.map((color) => (
162+
<button
163+
key={color.name}
164+
onClick={() => toggleColor(color.name)}
165+
className={`w-8 h-8 rounded-full ${color.class} ${
166+
selectedColors.includes(color.name)
167+
? 'ring-2 ring-offset-2 ring-gray-900'
168+
: ''
169+
}`}
170+
title={color.name}
171+
/>
172+
))}
173+
</div>
174+
</div>
175+
</div>
176+
</div>
177+
178+
{/* Main Content */}
179+
<div className="flex-1">
180+
<div className="flex justify-between items-center mb-8">
181+
<h1 className="text-2xl font-medium">
182+
Herreklær <span className="text-gray-500">({products.length})</span>
183+
</h1>
184+
185+
<div className="flex items-center gap-4">
186+
<label className="text-sm">Vis produkter:</label>
187+
<select
188+
value={sortBy}
189+
onChange={(e) => setSortBy(e.target.value)}
190+
className="border rounded-md px-3 py-1"
191+
>
192+
<option value="popular">Populær</option>
193+
<option value="price-low">Pris: Lav til Høy</option>
194+
<option value="price-high">Pris: Høy til Lav</option>
195+
<option value="newest">Nyeste</option>
196+
</select>
197+
</div>
198+
</div>
199+
200+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
201+
{products.map((product: Product) => (
202+
<ProductCard
203+
key={product.databaseId}
204+
databaseId={product.databaseId}
205+
name={product.name}
206+
price={product.price}
207+
slug={product.slug}
208+
image={product.image}
209+
/>
210+
))}
211+
</div>
212+
</div>
213+
</div>
214+
</div>
215+
</Layout>
216+
);
217+
};
218+
219+
export default Products2;
220+
221+
export const getStaticProps: GetStaticProps = async () => {
222+
const { data, loading, networkStatus } = await client.query({
223+
query: FETCH_ALL_PRODUCTS_QUERY,
224+
});
225+
226+
return {
227+
props: {
228+
products: data.products.nodes,
229+
loading,
230+
networkStatus,
231+
},
232+
revalidate: 60,
233+
};
234+
};

0 commit comments

Comments
 (0)