Skip to content

Commit 35f0dd2

Browse files
committed
Changed Product to Dialog
-Changed response from order-service -Changed UI in Metal-Front-End servcie
1 parent 1692d7d commit 35f0dd2

5 files changed

Lines changed: 257 additions & 24 deletions

File tree

apps/shop/metal-mart-frontend/src/app/globals.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,28 @@ a, button {
109109
opacity: 0.9;
110110
}
111111

112+
/* Dialog animations */
113+
@keyframes dialogBackdropIn {
114+
from { opacity: 0; }
115+
to { opacity: 1; }
116+
}
117+
@keyframes dialogPanelIn {
118+
from {
119+
opacity: 0;
120+
transform: scale(0.95) translateY(10px);
121+
}
122+
to {
123+
opacity: 1;
124+
transform: scale(1) translateY(0);
125+
}
126+
}
127+
.animate-dialog-backdrop {
128+
animation: dialogBackdropIn 0.2s ease-out forwards;
129+
}
130+
.animate-dialog-panel {
131+
animation: dialogPanelIn 0.25s ease-out forwards;
132+
}
133+
112134
/* Decorative wave above footer */
113135
.footer-wave {
114136
display: block;

apps/shop/metal-mart-frontend/src/app/page.tsx

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Header from "@/components/Header";
66
import LoadingSpinner from "@/components/LoadingSpinner";
77
import NewBadge from "@/components/NewBadge";
88
import ProductImage from "@/components/ProductImage";
9+
import ProductDialog from "@/components/ProductDialog";
910
import { getPrimaryImageUrl, type Product } from "@/lib/product";
1011

1112
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
@@ -15,23 +16,24 @@ function ProductTile({
1516
variant,
1617
delay = 0,
1718
elevated = false,
19+
onSelect,
1820
}: {
1921
product: Product;
2022
variant: "featured" | "standard" | "wide";
2123
delay?: number;
22-
/** Above mascot on home page so card stays opaque */
2324
elevated?: boolean;
25+
onSelect: (product: Product) => void;
2426
}) {
25-
const href = `/products/${product.id}`;
2627
const price = `$${(product.price_cents / 100).toFixed(2)}`;
2728

2829
const elevatedClass = elevated ? "relative z-30" : "";
2930

3031
if (variant === "featured") {
3132
return (
32-
<Link
33-
href={href}
34-
className={`group relative flex h-full min-h-[280px] flex-col overflow-hidden rounded-2xl border border-slate-300 bg-slate-100 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-xl hover:shadow-[#6a4ff5]/15 hover:border-[#6a4ff5]/30 animate-card-reveal ${elevatedClass}`}
33+
<button
34+
type="button"
35+
onClick={() => onSelect(product)}
36+
className={`group relative flex h-full min-h-[280px] w-full flex-col overflow-hidden rounded-2xl border border-slate-300 bg-slate-100 text-left shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-xl hover:shadow-[#6a4ff5]/15 hover:border-[#6a4ff5]/30 animate-card-reveal ${elevatedClass}`}
3537
style={{ animationDelay: `${delay}s` }}
3638
>
3739
{product.is_new && <NewBadge size="default" />}
@@ -60,15 +62,16 @@ function ProductTile({
6062
Shop now →
6163
</span>
6264
</div>
63-
</Link>
65+
</button>
6466
);
6567
}
6668

6769
if (variant === "wide") {
6870
return (
69-
<Link
70-
href={href}
71-
className={`group relative flex flex-col overflow-hidden rounded-2xl border border-slate-300 bg-white shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal sm:flex-row ${elevatedClass}`}
71+
<button
72+
type="button"
73+
onClick={() => onSelect(product)}
74+
className={`group relative flex w-full flex-col overflow-hidden rounded-2xl border border-slate-300 bg-white text-left shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal sm:flex-row ${elevatedClass}`}
7275
style={{ animationDelay: `${delay}s` }}
7376
>
7477
{product.is_new && <NewBadge size="default" />}
@@ -98,15 +101,16 @@ function ProductTile({
98101
</p>
99102
)}
100103
</div>
101-
</Link>
104+
</button>
102105
);
103106
}
104107

105108
// standard
106109
return (
107-
<Link
108-
href={href}
109-
className={`group relative flex flex-col overflow-hidden rounded-2xl border border-slate-300 bg-white shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal ${elevatedClass}`}
110+
<button
111+
type="button"
112+
onClick={() => onSelect(product)}
113+
className={`group relative flex w-full flex-col overflow-hidden rounded-2xl border border-slate-300 bg-white text-left shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal ${elevatedClass}`}
110114
style={{ animationDelay: `${delay}s` }}
111115
>
112116
{product.is_new && <NewBadge size="default" />}
@@ -131,14 +135,15 @@ function ProductTile({
131135
</h2>
132136
<p className="mt-1 font-semibold text-[#6a4ff5]">{price}</p>
133137
</div>
134-
</Link>
138+
</button>
135139
);
136140
}
137141

138142
export default function Home() {
139143
const [products, setProducts] = useState<Product[]>([]);
140144
const [loading, setLoading] = useState(true);
141145
const [error, setError] = useState<string | null>(null);
146+
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
142147

143148
useEffect(() => {
144149
fetch(`${basePath}/api/products`)
@@ -207,26 +212,26 @@ export default function Home() {
207212
: ""
208213
}
209214
>
210-
<ProductTile product={featured} variant="featured" delay={0} elevated />
215+
<ProductTile product={featured} variant="featured" delay={0} elevated onSelect={setSelectedProduct} />
211216
</div>
212217
)}
213218
{/* Side tiles - products 2 and 3 stacked on the right (or single tile spans 2 rows) */}
214219
{rest[0] && (
215220
<div
216221
className={`md:col-span-2 ${rest[1] ? "md:row-span-1" : "md:row-span-2"}`}
217222
>
218-
<ProductTile product={rest[0]} variant="standard" delay={0.06} elevated />
223+
<ProductTile product={rest[0]} variant="standard" delay={0.06} elevated onSelect={setSelectedProduct} />
219224
</div>
220225
)}
221226
{rest[1] && (
222227
<div className="md:col-span-2 md:row-span-1">
223-
<ProductTile product={rest[1]} variant="standard" delay={0.12} elevated />
228+
<ProductTile product={rest[1]} variant="standard" delay={0.12} elevated onSelect={setSelectedProduct} />
224229
</div>
225230
)}
226231
{/* Bottom row - product 4 as wide horizontal tile */}
227232
{rest[2] && (
228233
<div className="md:col-span-4">
229-
<ProductTile product={rest[2]} variant="wide" delay={0.18} elevated />
234+
<ProductTile product={rest[2]} variant="wide" delay={0.18} elevated onSelect={setSelectedProduct} />
230235
</div>
231236
)}
232237
{/* Products 5+ in a grid */}
@@ -239,6 +244,7 @@ export default function Home() {
239244
variant="standard"
240245
delay={0.24 + i * 0.06}
241246
elevated
247+
onSelect={setSelectedProduct}
242248
/>
243249
))}
244250
</div>
@@ -268,6 +274,13 @@ export default function Home() {
268274
</div>
269275
</section>
270276
</main>
277+
278+
{selectedProduct && (
279+
<ProductDialog
280+
product={selectedProduct}
281+
onClose={() => setSelectedProduct(null)}
282+
/>
283+
)}
271284
</div>
272285
);
273286
}

apps/shop/metal-mart-frontend/src/app/products/page.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"use client";
22

33
import { useEffect, useState } from "react";
4-
import Link from "next/link";
54
import Header from "@/components/Header";
65
import LoadingSpinner from "@/components/LoadingSpinner";
76
import NewBadge from "@/components/NewBadge";
87
import ProductImage from "@/components/ProductImage";
8+
import ProductDialog from "@/components/ProductDialog";
99
import { getPrimaryImageUrl, type Product } from "@/lib/product";
1010

1111
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
@@ -14,6 +14,7 @@ export default function ProductsPage() {
1414
const [products, setProducts] = useState<Product[]>([]);
1515
const [loading, setLoading] = useState(true);
1616
const [error, setError] = useState<string | null>(null);
17+
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
1718

1819
useEffect(() => {
1920
fetch(`${basePath}/api/products`)
@@ -56,10 +57,11 @@ export default function ProductsPage() {
5657
</h1>
5758
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
5859
{products.map((p, i) => (
59-
<Link
60+
<button
6061
key={p.id}
61-
href={`/products/${p.id}`}
62-
className="group relative flex flex-col overflow-hidden rounded-xl border border-slate-300 bg-white shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal"
62+
type="button"
63+
onClick={() => setSelectedProduct(p)}
64+
className="group relative flex flex-col overflow-hidden rounded-xl border border-slate-300 bg-white text-left shadow-sm transition-all duration-300 hover:-translate-y-1 hover:border-[#6a4ff5]/30 hover:shadow-xl hover:shadow-[#6a4ff5]/10 animate-card-reveal"
6365
style={{ animationDelay: `${i * 0.06}s` }}
6466
>
6567
{p.is_new && <NewBadge size="default" />}
@@ -85,11 +87,18 @@ export default function ProductsPage() {
8587
<p className="mt-2 text-lg font-semibold text-[#6a4ff5]">${(p.price_cents / 100).toFixed(2)}</p>
8688
<p className="mt-auto pt-3 text-xs text-slate-500">In stock: {p.stock}</p>
8789
</div>
88-
</Link>
90+
</button>
8991
))}
9092
</div>
9193
</div>
9294
</main>
95+
96+
{selectedProduct && (
97+
<ProductDialog
98+
product={selectedProduct}
99+
onClose={() => setSelectedProduct(null)}
100+
/>
101+
)}
93102
</div>
94103
);
95104
}

0 commit comments

Comments
 (0)