db hanta ktr mnhadchi chne bghiti
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
frontend/dist/
|
frontend/dist/
|
||||||
frontend/.vite/
|
frontend/.vite/
|
||||||
|
frontend/*.tsbuildinfo
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -14,10 +14,10 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1d2f7c0732dc29cde0fdc7f64a9699d72a431416")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f986e2387f42193e95687820c213e43880d84136")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyProductAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
// Generated by the MSBuild WriteCodeFragment class.
|
// Von der MSBuild WriteCodeFragment-Klasse generiert.
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
8939993bdc8bf465475db388d074eb82a687860b9f0504b4815bf206f612de33
|
41098eb382a4f44343bd406c25389fc17c93ba3401bf7b32e24159269779bdd2
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"jjFS3ypc+SFFIJoabqxBcUwQp4+BH7vO9y9aDuWO8Ig=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["oM\u002B92zWtRWs1ZZHQG5F\u002BZt\u002BmUwKFCmEHXl6yhJyDB9E=","m4ORQ/F\u002BR\u002BZG7hKB0ZiSTgsXFT54fbrxurDLZfuPLtU=","/26wWziwS0KH/O896xGXBSLufvhay7wSjV2zdTxws18=","WyWGi43zm6PntQI/ucnnU3lxt5XudSMI/Un84LZNCNI=","pzhO73lOLr0aoNbQo3AXbJ7qsA2VDRY9Il7vtsfu6/g=","\u002BmYXsDWUwEv8MDDi5d\u002BdcyyfamgV9HnHNS18DvTVT8g=","KmtVrMRHetgVdPC7Ti0TzXPRMK5M58DycF9gOmYQmuk="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"jjFS3ypc+SFFIJoabqxBcUwQp4+BH7vO9y9aDuWO8Ig=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["oM\u002B92zWtRWs1ZZHQG5F\u002BZt\u002BmUwKFCmEHXl6yhJyDB9E=","m4ORQ/F\u002BR\u002BZG7hKB0ZiSTgsXFT54fbrxurDLZfuPLtU=","/26wWziwS0KH/O896xGXBSLufvhay7wSjV2zdTxws18=","WyWGi43zm6PntQI/ucnnU3lxt5XudSMI/Un84LZNCNI=","pzhO73lOLr0aoNbQo3AXbJ7qsA2VDRY9Il7vtsfu6/g=","\u002BmYXsDWUwEv8MDDi5d\u002BdcyyfamgV9HnHNS18DvTVT8g=","Tm7CcfRRz8vmlt8l9mYtBKdJqqgOF4FJyGXnX/H8m3Q="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"tbgf6NkZHDHq5Bwz13FNLNq2Biw7YSKbQETYkFTRirw=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["oM\u002B92zWtRWs1ZZHQG5F\u002BZt\u002BmUwKFCmEHXl6yhJyDB9E=","m4ORQ/F\u002BR\u002BZG7hKB0ZiSTgsXFT54fbrxurDLZfuPLtU=","/26wWziwS0KH/O896xGXBSLufvhay7wSjV2zdTxws18=","WyWGi43zm6PntQI/ucnnU3lxt5XudSMI/Un84LZNCNI=","pzhO73lOLr0aoNbQo3AXbJ7qsA2VDRY9Il7vtsfu6/g=","\u002BmYXsDWUwEv8MDDi5d\u002BdcyyfamgV9HnHNS18DvTVT8g=","KmtVrMRHetgVdPC7Ti0TzXPRMK5M58DycF9gOmYQmuk="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"tbgf6NkZHDHq5Bwz13FNLNq2Biw7YSKbQETYkFTRirw=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["oM\u002B92zWtRWs1ZZHQG5F\u002BZt\u002BmUwKFCmEHXl6yhJyDB9E=","m4ORQ/F\u002BR\u002BZG7hKB0ZiSTgsXFT54fbrxurDLZfuPLtU=","/26wWziwS0KH/O896xGXBSLufvhay7wSjV2zdTxws18=","WyWGi43zm6PntQI/ucnnU3lxt5XudSMI/Un84LZNCNI=","pzhO73lOLr0aoNbQo3AXbJ7qsA2VDRY9Il7vtsfu6/g=","\u002BmYXsDWUwEv8MDDi5d\u002BdcyyfamgV9HnHNS18DvTVT8g=","Tm7CcfRRz8vmlt8l9mYtBKdJqqgOF4FJyGXnX/H8m3Q="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1,78 +1,612 @@
|
|||||||
import type { AuthUser, Category, Order, Product } from "../types";
|
import type { AuthUser, Category, Order, Product } from "../types";
|
||||||
|
|
||||||
export const mockCategories: Category[] = [
|
const photo = (seed: string) => `https://picsum.photos/seed/${encodeURIComponent(seed)}/900/700`;
|
||||||
{ id: 1, name: "Tech Essentials" },
|
|
||||||
{ id: 2, name: "Studio Setup" },
|
|
||||||
{ id: 3, name: "Smart Living" },
|
|
||||||
{ id: 4, name: "Travel Picks" }
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mockProducts: Product[] = [
|
export const mockCategories: Category[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Flux One Headphones",
|
name: "Audio",
|
||||||
description:
|
description: "Headphones, speakers, and all-day listening essentials.",
|
||||||
"Wireless over-ear headphones tuned for deep focus sessions, clear calls, and long battery life.",
|
image: photo("fluxon-audio-category")
|
||||||
price: 179.9,
|
|
||||||
stock: 14,
|
|
||||||
categoryId: 1,
|
|
||||||
image: "linear-gradient(135deg, #ff8a5b 0%, #ffd166 100%)",
|
|
||||||
featured: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "Nova Desk Lamp",
|
name: "Smart Devices",
|
||||||
description:
|
description: "Wearables and connected products for modern routines.",
|
||||||
"A sculptural LED lamp with warm-to-cool temperature control for late-night work and clean desk aesthetics.",
|
image: photo("fluxon-smart-devices-category")
|
||||||
price: 69,
|
|
||||||
stock: 22,
|
|
||||||
categoryId: 2,
|
|
||||||
image: "linear-gradient(135deg, #0f4c81 0%, #9bd1e5 100%)",
|
|
||||||
featured: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "Orbit Speaker Mini",
|
name: "Accessories",
|
||||||
description:
|
description: "Chargers, stands, cables, and practical daily add-ons.",
|
||||||
"Portable speaker with punchy sound, matte finish, and enough battery to last through weekend trips.",
|
image: photo("fluxon-accessories-category")
|
||||||
price: 95.5,
|
|
||||||
stock: 8,
|
|
||||||
categoryId: 4,
|
|
||||||
image: "linear-gradient(135deg, #23395d 0%, #b6c9f0 100%)",
|
|
||||||
featured: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
name: "Aero Bottle",
|
name: "Home Tech",
|
||||||
description:
|
description: "Useful tech for comfort, lighting, cleaning, and air quality.",
|
||||||
"Insulated stainless steel bottle designed for city commutes, travel, and everyday carry.",
|
image: photo("fluxon-home-tech-category")
|
||||||
price: 32,
|
|
||||||
stock: 40,
|
|
||||||
categoryId: 4,
|
|
||||||
image: "linear-gradient(135deg, #2a6f97 0%, #61c0bf 100%)"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
name: "Canvas Keyboard",
|
name: "Personal Care",
|
||||||
description:
|
description: "Smart grooming and self-care tools with a premium feel.",
|
||||||
"Compact mechanical keyboard with low-profile switches and a clean, minimalist layout.",
|
image: photo("fluxon-personal-care-category")
|
||||||
price: 124,
|
|
||||||
stock: 16,
|
|
||||||
categoryId: 1,
|
|
||||||
image: "linear-gradient(135deg, #5f0f40 0%, #fb8b24 100%)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: "Pulse Air Purifier",
|
|
||||||
description:
|
|
||||||
"Smart purifier with quiet mode, room-quality indicator, and app-ready control concept.",
|
|
||||||
price: 210,
|
|
||||||
stock: 11,
|
|
||||||
categoryId: 3,
|
|
||||||
image: "linear-gradient(135deg, #355070 0%, #b56576 100%)"
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type ProductSeed = Omit<Product, "id" | "categoryId" | "category"> & { categoryName: Category["name"] };
|
||||||
|
|
||||||
|
const productSeeds: ProductSeed[] = [
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "FluxBuds Air",
|
||||||
|
description: "True wireless earbuds with balanced sound and compact charging case.",
|
||||||
|
price: 69,
|
||||||
|
oldPrice: 89,
|
||||||
|
stock: 32,
|
||||||
|
badge: "New",
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 214,
|
||||||
|
featured: true,
|
||||||
|
newArrival: true,
|
||||||
|
image: photo("fluxon-audio-1")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "NovaSound ANC",
|
||||||
|
description: "Over-ear headphones with active noise cancellation and 40-hour playback.",
|
||||||
|
price: 149,
|
||||||
|
oldPrice: 189,
|
||||||
|
stock: 18,
|
||||||
|
badge: "Best Seller",
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 843,
|
||||||
|
featured: true,
|
||||||
|
bestSeller: true,
|
||||||
|
image: photo("fluxon-audio-2")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "PulseBeat Mini",
|
||||||
|
description: "Pocket speaker with rich bass tuning and splash-resistant finish.",
|
||||||
|
price: 54,
|
||||||
|
stock: 25,
|
||||||
|
badge: "Sale",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 133,
|
||||||
|
image: photo("fluxon-audio-3")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "WaveLoop Pro",
|
||||||
|
description: "Neckband earphones designed for calls, commuting, and long battery life.",
|
||||||
|
price: 39,
|
||||||
|
oldPrice: 49,
|
||||||
|
stock: 26,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 91,
|
||||||
|
image: photo("fluxon-audio-4")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "StudioArc Wired",
|
||||||
|
description: "Wired headphones with clean detail and lightweight studio-inspired design.",
|
||||||
|
price: 59,
|
||||||
|
stock: 21,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 74,
|
||||||
|
image: photo("fluxon-audio-5")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "GameTone X",
|
||||||
|
description: "Gaming headset with soft cushions, boom mic, and immersive stereo sound.",
|
||||||
|
price: 79,
|
||||||
|
oldPrice: 99,
|
||||||
|
stock: 17,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 205,
|
||||||
|
image: photo("fluxon-audio-6")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "RoomBar Slim",
|
||||||
|
description: "Minimal soundbar built for compact desks, bedrooms, and small TVs.",
|
||||||
|
price: 119,
|
||||||
|
stock: 10,
|
||||||
|
badge: "Hot",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 168,
|
||||||
|
image: photo("fluxon-audio-7")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "EchoDot Pocket",
|
||||||
|
description: "Mini speaker with soft-touch finish and quick-connect wireless pairing.",
|
||||||
|
price: 35,
|
||||||
|
stock: 40,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 52,
|
||||||
|
image: photo("fluxon-audio-8")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "QuietPods Max",
|
||||||
|
description: "Premium earbuds with deep ANC, transparency mode, and fast USB-C charging.",
|
||||||
|
price: 109,
|
||||||
|
oldPrice: 129,
|
||||||
|
stock: 15,
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 479,
|
||||||
|
image: photo("fluxon-audio-9")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Audio",
|
||||||
|
name: "Reference One",
|
||||||
|
description: "Closed-back headphones for focused work, editing, and crisp detail.",
|
||||||
|
price: 129,
|
||||||
|
stock: 8,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 118,
|
||||||
|
image: photo("fluxon-audio-10")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "Flux Watch S1",
|
||||||
|
description: "Smartwatch with AMOLED display, call support, and daily health tracking.",
|
||||||
|
price: 139,
|
||||||
|
oldPrice: 169,
|
||||||
|
stock: 22,
|
||||||
|
badge: "Best Seller",
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 692,
|
||||||
|
featured: true,
|
||||||
|
bestSeller: true,
|
||||||
|
image: photo("fluxon-smart-1")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "PulseBand Fit",
|
||||||
|
description: "Slim fitness band with sleep tracking, step count, and workout reminders.",
|
||||||
|
price: 49,
|
||||||
|
stock: 38,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 175,
|
||||||
|
image: photo("fluxon-smart-2")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "Halo Ring Lite",
|
||||||
|
description: "Smart ring concept for sleep tracking and low-profile daily wear.",
|
||||||
|
price: 99,
|
||||||
|
oldPrice: 119,
|
||||||
|
stock: 11,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 42,
|
||||||
|
image: photo("fluxon-smart-3")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "DeskHub Go",
|
||||||
|
description: "Compact smart desk hub with clock, USB ports, and workspace shortcuts.",
|
||||||
|
price: 89,
|
||||||
|
stock: 14,
|
||||||
|
rating: 4.4,
|
||||||
|
reviewCount: 36,
|
||||||
|
image: photo("fluxon-smart-4")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "WakeCube",
|
||||||
|
description: "Smart alarm clock with ambient light and gentle morning routines.",
|
||||||
|
price: 59,
|
||||||
|
stock: 29,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 103,
|
||||||
|
image: photo("fluxon-smart-5")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "TrackTag Duo",
|
||||||
|
description: "Bluetooth tracker set for keys, backpacks, and travel essentials.",
|
||||||
|
price: 29,
|
||||||
|
oldPrice: 39,
|
||||||
|
stock: 47,
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 318,
|
||||||
|
image: photo("fluxon-smart-6")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "Vista Mini",
|
||||||
|
description: "Portable smart display with clean interface for weather, timers, and media.",
|
||||||
|
price: 119,
|
||||||
|
stock: 9,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 65,
|
||||||
|
image: photo("fluxon-smart-7")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "HealthSync Band",
|
||||||
|
description: "Daily wellness band focused on recovery insights and heart tracking.",
|
||||||
|
price: 65,
|
||||||
|
stock: 19,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 98,
|
||||||
|
image: photo("fluxon-smart-8")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "Balance Scale Pro",
|
||||||
|
description: "Smart body scale with composition trends and companion-app readiness.",
|
||||||
|
price: 79,
|
||||||
|
stock: 13,
|
||||||
|
badge: "Popular",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 151,
|
||||||
|
image: photo("fluxon-smart-9")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Smart Devices",
|
||||||
|
name: "Beam Pocket",
|
||||||
|
description: "Mini projector made for flexible movie nights and portable presentations.",
|
||||||
|
price: 199,
|
||||||
|
oldPrice: 239,
|
||||||
|
stock: 7,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 87,
|
||||||
|
featured: true,
|
||||||
|
image: photo("fluxon-smart-10")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "VoltCharge 35",
|
||||||
|
description: "Fast wall charger with compact form and dual-device charging.",
|
||||||
|
price: 29,
|
||||||
|
stock: 56,
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 521,
|
||||||
|
image: photo("fluxon-accessory-1")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "MagBank 10K",
|
||||||
|
description: "Magnetic power bank sized for travel days and fast top-ups.",
|
||||||
|
price: 69,
|
||||||
|
oldPrice: 89,
|
||||||
|
stock: 31,
|
||||||
|
badge: "Best Seller",
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 405,
|
||||||
|
featured: true,
|
||||||
|
bestSeller: true,
|
||||||
|
image: photo("fluxon-accessory-2")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "Braided Link C",
|
||||||
|
description: "Durable braided USB-C cable designed for desks, bags, and charging stations.",
|
||||||
|
price: 15,
|
||||||
|
stock: 82,
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 210,
|
||||||
|
image: photo("fluxon-accessory-3")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "AirDock Pad",
|
||||||
|
description: "Minimal wireless charging pad with soft LED ring and anti-slip base.",
|
||||||
|
price: 34,
|
||||||
|
stock: 28,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 88,
|
||||||
|
image: photo("fluxon-accessory-4")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "Rise Laptop Stand",
|
||||||
|
description: "Foldable aluminum stand for cleaner posture and cooler laptop airflow.",
|
||||||
|
price: 42,
|
||||||
|
stock: 23,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 144,
|
||||||
|
image: photo("fluxon-accessory-5")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "FlexGrip Holder",
|
||||||
|
description: "Adjustable phone holder for desks, kitchens, and bedside use.",
|
||||||
|
price: 19,
|
||||||
|
stock: 37,
|
||||||
|
rating: 4.4,
|
||||||
|
reviewCount: 61,
|
||||||
|
image: photo("fluxon-accessory-6")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "TravelPlug World",
|
||||||
|
description: "Travel adapter with clean design and multi-region compatibility.",
|
||||||
|
price: 39,
|
||||||
|
oldPrice: 49,
|
||||||
|
stock: 16,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 57,
|
||||||
|
image: photo("fluxon-accessory-7")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "KeySleeve Pro",
|
||||||
|
description: "Protective keyboard sleeve with felt lining and slim profile.",
|
||||||
|
price: 24,
|
||||||
|
stock: 26,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 41,
|
||||||
|
image: photo("fluxon-accessory-8")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "PortHub 6",
|
||||||
|
description: "Six-port USB hub for workstations that need flexible connectivity.",
|
||||||
|
price: 49,
|
||||||
|
stock: 21,
|
||||||
|
badge: "Hot",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 129,
|
||||||
|
image: photo("fluxon-accessory-9")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Accessories",
|
||||||
|
name: "Shield Case Clear",
|
||||||
|
description: "Transparent protective phone case with reinforced corners.",
|
||||||
|
price: 18,
|
||||||
|
stock: 44,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.3,
|
||||||
|
reviewCount: 39,
|
||||||
|
image: photo("fluxon-accessory-10")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "PureFlow Air",
|
||||||
|
description: "Compact air purifier for bedrooms, workspaces, and quiet evenings.",
|
||||||
|
price: 189,
|
||||||
|
oldPrice: 229,
|
||||||
|
stock: 12,
|
||||||
|
featured: true,
|
||||||
|
badge: "Best Seller",
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 366,
|
||||||
|
image: photo("fluxon-home-1")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "Luma Desk Beam",
|
||||||
|
description: "LED desk lamp with adjustable warmth and a clean architectural silhouette.",
|
||||||
|
price: 64,
|
||||||
|
stock: 34,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 202,
|
||||||
|
image: photo("fluxon-home-2")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "SweepBot Mini",
|
||||||
|
description: "Robot vacuum built for daily dust pickup in compact living spaces.",
|
||||||
|
price: 249,
|
||||||
|
oldPrice: 299,
|
||||||
|
stock: 8,
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 157,
|
||||||
|
image: photo("fluxon-home-3")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "MistCore",
|
||||||
|
description: "Quiet humidifier with soft light and modern bedside presence.",
|
||||||
|
price: 55,
|
||||||
|
stock: 20,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 84,
|
||||||
|
image: photo("fluxon-home-4")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "Aroma Glow",
|
||||||
|
description: "Aroma diffuser combining gentle vapor flow with ambient mood lighting.",
|
||||||
|
price: 39,
|
||||||
|
stock: 24,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 72,
|
||||||
|
image: photo("fluxon-home-5")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "CoolFold Fan",
|
||||||
|
description: "Portable fan with foldable body and desk-friendly footprint.",
|
||||||
|
price: 45,
|
||||||
|
stock: 30,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 93,
|
||||||
|
image: photo("fluxon-home-6")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "GlowBulb Kit",
|
||||||
|
description: "Smart bulb starter kit for mood shifts, desk scenes, and warm evenings.",
|
||||||
|
price: 79,
|
||||||
|
stock: 18,
|
||||||
|
badge: "Popular",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 147,
|
||||||
|
image: photo("fluxon-home-7")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "WarmSpace Mini",
|
||||||
|
description: "Compact heater designed for focused work corners and cool mornings.",
|
||||||
|
price: 99,
|
||||||
|
oldPrice: 119,
|
||||||
|
stock: 11,
|
||||||
|
rating: 4.4,
|
||||||
|
reviewCount: 48,
|
||||||
|
image: photo("fluxon-home-8")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "ClearTap Filter",
|
||||||
|
description: "Countertop water filter unit with simple control and clean lines.",
|
||||||
|
price: 129,
|
||||||
|
stock: 9,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 67,
|
||||||
|
image: photo("fluxon-home-9")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Home Tech",
|
||||||
|
name: "DustGo Handheld",
|
||||||
|
description: "Handheld mini vacuum for shelves, keyboards, and quick spot cleaning.",
|
||||||
|
price: 59,
|
||||||
|
stock: 27,
|
||||||
|
newArrival: true,
|
||||||
|
featured: true,
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 112,
|
||||||
|
image: photo("fluxon-home-10")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "TrimEdge Pro",
|
||||||
|
description: "Cordless beard trimmer with precise guide combs and sharp stainless blades.",
|
||||||
|
price: 69,
|
||||||
|
oldPrice: 89,
|
||||||
|
stock: 22,
|
||||||
|
badge: "Best Seller",
|
||||||
|
featured: true,
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.9,
|
||||||
|
reviewCount: 553,
|
||||||
|
image: photo("fluxon-care-1")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "SilkDry Air",
|
||||||
|
description: "Hair dryer with sleek build, fast airflow, and low-frizz styling mode.",
|
||||||
|
price: 99,
|
||||||
|
stock: 19,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 188,
|
||||||
|
image: photo("fluxon-care-2")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "PureFace Brush",
|
||||||
|
description: "Facial cleansing brush for a softer skincare routine and clean countertop look.",
|
||||||
|
price: 34,
|
||||||
|
stock: 31,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 77,
|
||||||
|
image: photo("fluxon-care-3")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "BodyLine Groomer",
|
||||||
|
description: "Water-resistant body groomer built for comfortable weekly upkeep.",
|
||||||
|
price: 49,
|
||||||
|
stock: 25,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 90,
|
||||||
|
image: photo("fluxon-care-4")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "ReliefPulse Mini",
|
||||||
|
description: "Compact massage gun for post-workout recovery and daily neck tension relief.",
|
||||||
|
price: 119,
|
||||||
|
oldPrice: 149,
|
||||||
|
stock: 13,
|
||||||
|
bestSeller: true,
|
||||||
|
rating: 4.8,
|
||||||
|
reviewCount: 209,
|
||||||
|
image: photo("fluxon-care-5")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "SmoothTouch Epilator",
|
||||||
|
description: "Epilator with travel cap, dual speeds, and ergonomic body shape.",
|
||||||
|
price: 79,
|
||||||
|
stock: 17,
|
||||||
|
newArrival: true,
|
||||||
|
rating: 4.5,
|
||||||
|
reviewCount: 58,
|
||||||
|
image: photo("fluxon-care-6")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "BeardCare Kit",
|
||||||
|
description: "Premium beard kit with trimmer tools and simple daily maintenance feel.",
|
||||||
|
price: 59,
|
||||||
|
stock: 28,
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 101,
|
||||||
|
image: photo("fluxon-care-7")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "SleekIron One",
|
||||||
|
description: "Hair straightener with smooth plates and salon-inspired matte finish.",
|
||||||
|
price: 69,
|
||||||
|
stock: 15,
|
||||||
|
rating: 4.6,
|
||||||
|
reviewCount: 69,
|
||||||
|
image: photo("fluxon-care-8")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "SonicBrush Daily",
|
||||||
|
description: "Sonic toothbrush with travel case and two brushing intensity modes.",
|
||||||
|
price: 45,
|
||||||
|
stock: 33,
|
||||||
|
badge: "Popular",
|
||||||
|
rating: 4.7,
|
||||||
|
reviewCount: 142,
|
||||||
|
image: photo("fluxon-care-9")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: "Personal Care",
|
||||||
|
name: "NailStudio Go",
|
||||||
|
description: "Portable manicure device built for neat, at-home finishing touches.",
|
||||||
|
price: 29,
|
||||||
|
stock: 30,
|
||||||
|
newArrival: true,
|
||||||
|
featured: true,
|
||||||
|
rating: 4.4,
|
||||||
|
reviewCount: 33,
|
||||||
|
image: photo("fluxon-care-10")
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const mockProducts: Product[] = productSeeds.map((seed, index) => {
|
||||||
|
const category = mockCategories.find((item) => item.name === seed.categoryName)!;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: index + 1,
|
||||||
|
categoryId: category.id,
|
||||||
|
category,
|
||||||
|
...seed
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
export const mockDemoUser: AuthUser = {
|
export const mockDemoUser: AuthUser = {
|
||||||
name: "Demo Customer",
|
name: "Demo Customer",
|
||||||
email: "demo@fluxon.shop",
|
email: "demo@fluxon.shop",
|
||||||
@@ -87,21 +621,27 @@ export const mockOrders: Order[] = [
|
|||||||
total: 248.9,
|
total: 248.9,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
productId: 1,
|
productId: 2,
|
||||||
productName: "Flux One Headphones",
|
productName: "NovaSound ANC",
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
unitPrice: 179.9
|
unitPrice: 149
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
productId: 4,
|
productId: 21,
|
||||||
productName: "Aero Bottle",
|
productName: "MagBank 10K",
|
||||||
quantity: 2,
|
quantity: 1,
|
||||||
unitPrice: 32
|
unitPrice: 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
productId: 43,
|
||||||
|
productName: "ReliefPulse Mini",
|
||||||
|
quantity: 1,
|
||||||
|
unitPrice: 119
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
payment: {
|
payment: {
|
||||||
method: "PayPal",
|
method: "PayPal",
|
||||||
amount: 248.9,
|
amount: 337,
|
||||||
status: "Paid"
|
status: "Paid"
|
||||||
},
|
},
|
||||||
customerEmail: "demo@fluxon.shop",
|
customerEmail: "demo@fluxon.shop",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Link } from "react-router-dom";
|
|||||||
import { useCart } from "../state/CartContext";
|
import { useCart } from "../state/CartContext";
|
||||||
import { QuantityControl } from "../ui/QuantityControl";
|
import { QuantityControl } from "../ui/QuantityControl";
|
||||||
import { StatusView } from "../ui/StatusView";
|
import { StatusView } from "../ui/StatusView";
|
||||||
|
import { visualStyle } from "../ui/visuals";
|
||||||
|
|
||||||
export function CartPage() {
|
export function CartPage() {
|
||||||
const { items, removeItem, updateQuantity, subtotal } = useCart();
|
const { items, removeItem, updateQuantity, subtotal } = useCart();
|
||||||
@@ -33,7 +34,7 @@ export function CartPage() {
|
|||||||
<div className="cart-list">
|
<div className="cart-list">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<article key={item.product.id} className="cart-row">
|
<article key={item.product.id} className="cart-row">
|
||||||
<div className="cart-visual" style={{ background: item.product.image }} />
|
<div className="cart-visual" style={visualStyle(item.product.image)} />
|
||||||
<div className="cart-copy">
|
<div className="cart-copy">
|
||||||
<h3>{item.product.name}</h3>
|
<h3>{item.product.name}</h3>
|
||||||
<p>{item.product.description}</p>
|
<p>{item.product.description}</p>
|
||||||
|
|||||||
@@ -16,39 +16,41 @@ export function HomePage() {
|
|||||||
return <StatusView title="Catalog unavailable" message={error} />;
|
return <StatusView title="Catalog unavailable" message={error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const featuredProducts = products.filter((product) => product.featured).slice(0, 3);
|
const featuredProducts = products.filter((product) => product.featured).slice(0, 4);
|
||||||
|
const bestSellers = products.filter((product) => product.bestSeller).slice(0, 4);
|
||||||
|
const newArrivals = products.filter((product) => product.newArrival).slice(0, 4);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-stack">
|
<div className="page-stack">
|
||||||
<section className="hero-panel">
|
<section className="hero-panel">
|
||||||
<div className="hero-copy">
|
<div className="hero-copy">
|
||||||
<span className="eyebrow">Fluxon storefront</span>
|
<span className="eyebrow">Next-Gen Everyday Tech</span>
|
||||||
<h1>Build a demo-ready shop now, then swap in real APIs as the backend catches up.</h1>
|
<h1>Smart products built for modern daily life.</h1>
|
||||||
<p>
|
<p>
|
||||||
This frontend is structured for hybrid delivery: polished enough for presentation, flexible enough for
|
Discover a curated storefront across Audio, Smart Devices, Accessories, Home Tech, and Personal Care with
|
||||||
incomplete endpoints.
|
a presentation-ready shopping flow.
|
||||||
</p>
|
</p>
|
||||||
<div className="inline-actions">
|
<div className="inline-actions">
|
||||||
<Link to="/products" className="cta-button">
|
<Link to="/products" className="cta-button">
|
||||||
Explore Products
|
Shop Now
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/register" className="ghost-button">
|
<Link to="/products" className="ghost-button">
|
||||||
Create Demo Account
|
Explore Categories
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-card-grid">
|
<div className="hero-card-grid">
|
||||||
<div className="hero-stat">
|
<div className="hero-stat">
|
||||||
<strong>{products.length}</strong>
|
<strong>{products.length}</strong>
|
||||||
<span>Curated products</span>
|
<span>Store-ready products</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-stat">
|
<div className="hero-stat">
|
||||||
<strong>{categories.length}</strong>
|
<strong>{categories.length}</strong>
|
||||||
<span>Shop categories</span>
|
<span>Core categories</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-stat accent">
|
<div className="hero-stat accent">
|
||||||
<strong>Mock + API</strong>
|
<strong>Deep Navy + Orange</strong>
|
||||||
<span>Safe for evolving backend</span>
|
<span>Clean tech-commerce direction</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -57,14 +59,17 @@ export function HomePage() {
|
|||||||
<div className="section-heading">
|
<div className="section-heading">
|
||||||
<div>
|
<div>
|
||||||
<span className="eyebrow">Categories</span>
|
<span className="eyebrow">Categories</span>
|
||||||
<h2>Shape the browsing experience around the backend data model</h2>
|
<h2>Shop the five storefront pillars of Fluxon</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="category-grid">
|
<div className="category-grid">
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<article key={category.id} className="category-card">
|
<article key={category.id} className="category-card">
|
||||||
<h3>{category.name}</h3>
|
<h3>{category.name}</h3>
|
||||||
<p>Use this category as a filter, navigation cue, and featured-content block.</p>
|
<p>{category.description}</p>
|
||||||
|
<Link to={`/products`} className="category-link">
|
||||||
|
Explore
|
||||||
|
</Link>
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -74,7 +79,7 @@ export function HomePage() {
|
|||||||
<div className="section-heading">
|
<div className="section-heading">
|
||||||
<div>
|
<div>
|
||||||
<span className="eyebrow">Featured</span>
|
<span className="eyebrow">Featured</span>
|
||||||
<h2>Presentation-friendly products for your MVP demo flow</h2>
|
<h2>Featured picks for the homepage hero flow</h2>
|
||||||
</div>
|
</div>
|
||||||
<Link to="/products" className="ghost-button">
|
<Link to="/products" className="ghost-button">
|
||||||
View All
|
View All
|
||||||
@@ -86,6 +91,72 @@ export function HomePage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="promo-band">
|
||||||
|
<article className="promo-card">
|
||||||
|
<span className="eyebrow">Promo</span>
|
||||||
|
<h3>Up to 30% off selected Audio and Accessories</h3>
|
||||||
|
<p>Use this section for discounts, launches, or your strongest weekly campaign.</p>
|
||||||
|
<Link to="/products" className="ghost-button">
|
||||||
|
See Offers
|
||||||
|
</Link>
|
||||||
|
</article>
|
||||||
|
<article className="promo-card promo-card-dark">
|
||||||
|
<span className="eyebrow">New Drop</span>
|
||||||
|
<h3>Fresh Smart Devices and Personal Care arrivals</h3>
|
||||||
|
<p>Keep the homepage feeling alive with fast-moving campaign blocks like this one.</p>
|
||||||
|
<Link to="/products" className="cta-button">
|
||||||
|
Browse New Arrivals
|
||||||
|
</Link>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="panel">
|
||||||
|
<div className="section-heading">
|
||||||
|
<div>
|
||||||
|
<span className="eyebrow">Best Sellers</span>
|
||||||
|
<h2>Products that add social proof to your storefront</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="product-grid">
|
||||||
|
{bestSellers.map((product) => (
|
||||||
|
<ProductCard key={product.id} product={product} onAdd={addItem} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="panel">
|
||||||
|
<div className="section-heading">
|
||||||
|
<div>
|
||||||
|
<span className="eyebrow">New Arrivals</span>
|
||||||
|
<h2>Fresh additions to keep the shop feeling current</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="product-grid">
|
||||||
|
{newArrivals.map((product) => (
|
||||||
|
<ProductCard key={product.id} product={product} onAdd={addItem} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="trust-grid">
|
||||||
|
<article className="trust-card">
|
||||||
|
<h3>Secure Checkout</h3>
|
||||||
|
<p>Use this block to reassure users that payments and account actions are protected.</p>
|
||||||
|
</article>
|
||||||
|
<article className="trust-card">
|
||||||
|
<h3>Fast Delivery</h3>
|
||||||
|
<p>Highlight delivery expectations and shipping confidence early in the homepage flow.</p>
|
||||||
|
</article>
|
||||||
|
<article className="trust-card">
|
||||||
|
<h3>Curated Products</h3>
|
||||||
|
<p>Show that Fluxon is not random inventory, but a selected modern tech catalog.</p>
|
||||||
|
</article>
|
||||||
|
<article className="trust-card">
|
||||||
|
<h3>Helpful Support</h3>
|
||||||
|
<p>Good support messaging makes a student project feel more like a real commerce brand.</p>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { Product } from "../types";
|
|||||||
import { StatusView } from "../ui/StatusView";
|
import { StatusView } from "../ui/StatusView";
|
||||||
import { QuantityControl } from "../ui/QuantityControl";
|
import { QuantityControl } from "../ui/QuantityControl";
|
||||||
import { useCart } from "../state/CartContext";
|
import { useCart } from "../state/CartContext";
|
||||||
|
import { visualStyle } from "../ui/visuals";
|
||||||
|
|
||||||
export function ProductDetailsPage() {
|
export function ProductDetailsPage() {
|
||||||
const { productId } = useParams();
|
const { productId } = useParams();
|
||||||
@@ -67,15 +68,22 @@ export function ProductDetailsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="product-detail-layout">
|
<section className="product-detail-layout">
|
||||||
<div className="detail-visual" style={{ background: product.image }} />
|
<div className="detail-visual" style={visualStyle(product.image)} />
|
||||||
<div className="detail-copy panel">
|
<div className="detail-copy panel">
|
||||||
<span className="eyebrow">{product.category?.name ?? "Product details"}</span>
|
<span className="eyebrow">{product.category?.name ?? "Product details"}</span>
|
||||||
<h1>{product.name}</h1>
|
<h1>{product.name}</h1>
|
||||||
<p className="lead">{product.description}</p>
|
<p className="lead">{product.description}</p>
|
||||||
|
<div className="rating-row">
|
||||||
|
<span>{product.rating ? `${product.rating.toFixed(1)} / 5` : "New item"}</span>
|
||||||
|
<span>{product.reviewCount ? `${product.reviewCount} verified reviews` : "Fresh listing"}</span>
|
||||||
|
</div>
|
||||||
<div className="detail-meta">
|
<div className="detail-meta">
|
||||||
<div>
|
<div>
|
||||||
<span>Price</span>
|
<span>Price</span>
|
||||||
|
<div className="price-stack">
|
||||||
<strong>EUR {product.price.toFixed(2)}</strong>
|
<strong>EUR {product.price.toFixed(2)}</strong>
|
||||||
|
{product.oldPrice ? <span>EUR {product.oldPrice.toFixed(2)}</span> : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>Availability</span>
|
<span>Availability</span>
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ export function ProductsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
|
case "new":
|
||||||
|
next.sort((a, b) => Number(b.newArrival) - Number(a.newArrival));
|
||||||
|
break;
|
||||||
|
case "best":
|
||||||
|
next.sort((a, b) => Number(b.bestSeller) - Number(a.bestSeller));
|
||||||
|
break;
|
||||||
case "price-asc":
|
case "price-asc":
|
||||||
next.sort((a, b) => a.price - b.price);
|
next.sort((a, b) => a.price - b.price);
|
||||||
break;
|
break;
|
||||||
@@ -72,6 +78,8 @@ export function ProductsPage() {
|
|||||||
/>
|
/>
|
||||||
<select value={sort} onChange={(event) => setSort(event.target.value)} className="select-input">
|
<select value={sort} onChange={(event) => setSort(event.target.value)} className="select-input">
|
||||||
<option value="featured">Featured first</option>
|
<option value="featured">Featured first</option>
|
||||||
|
<option value="best">Best sellers</option>
|
||||||
|
<option value="new">New arrivals</option>
|
||||||
<option value="price-asc">Price: low to high</option>
|
<option value="price-asc">Price: low to high</option>
|
||||||
<option value="price-desc">Price: high to low</option>
|
<option value="price-desc">Price: high to low</option>
|
||||||
<option value="stock">Most in stock</option>
|
<option value="stock">Most in stock</option>
|
||||||
|
|||||||
@@ -309,6 +309,13 @@ label {
|
|||||||
margin: 0 0 0.35rem;
|
margin: 0 0 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-link {
|
||||||
|
display: inline-flex;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
color: var(--accent-deep);
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
}
|
}
|
||||||
@@ -329,12 +336,51 @@ label {
|
|||||||
min-height: 220px;
|
min-height: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-visual {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
padding: 0.45rem 0.75rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(19, 34, 56, 0.88);
|
||||||
|
color: white;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
.product-copy {
|
.product-copy {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rating-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
color: var(--ink-soft);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-stack {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-stack span {
|
||||||
|
color: var(--ink-soft);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
.chip,
|
.chip,
|
||||||
.stock,
|
.stock,
|
||||||
.category-pill {
|
.category-pill {
|
||||||
@@ -510,6 +556,52 @@ label {
|
|||||||
background: rgba(255, 255, 255, 0.6);
|
background: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.promo-band,
|
||||||
|
.trust-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-band {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-card,
|
||||||
|
.trust-card {
|
||||||
|
padding: 28px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-card {
|
||||||
|
background: linear-gradient(135deg, rgba(255, 122, 69, 0.12) 0%, rgba(255, 255, 255, 0.9) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-card-dark {
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(135deg, #132238 0%, #20364f 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.promo-card-dark p,
|
||||||
|
.promo-card-dark .eyebrow,
|
||||||
|
.promo-card-dark h3 {
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-grid {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-card {
|
||||||
|
background: rgba(255, 255, 255, 0.78);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-card h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-grid {
|
.footer-grid {
|
||||||
grid-template-columns: repeat(3, auto);
|
grid-template-columns: repeat(3, auto);
|
||||||
align-content: center;
|
align-content: center;
|
||||||
@@ -519,6 +611,8 @@ label {
|
|||||||
.hero-panel,
|
.hero-panel,
|
||||||
.product-detail-layout,
|
.product-detail-layout,
|
||||||
.checkout-layout,
|
.checkout-layout,
|
||||||
|
.promo-band,
|
||||||
|
.trust-grid,
|
||||||
.site-footer {
|
.site-footer {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
@@ -573,6 +667,7 @@ label {
|
|||||||
.section-heading,
|
.section-heading,
|
||||||
.toolbar,
|
.toolbar,
|
||||||
.product-actions,
|
.product-actions,
|
||||||
|
.rating-row,
|
||||||
.meta-row,
|
.meta-row,
|
||||||
.inline-actions,
|
.inline-actions,
|
||||||
.detail-meta,
|
.detail-meta,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
export type Category = {
|
export type Category = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
image?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Product = {
|
export type Product = {
|
||||||
@@ -8,11 +10,17 @@ export type Product = {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
price: number;
|
price: number;
|
||||||
|
oldPrice?: number;
|
||||||
stock: number;
|
stock: number;
|
||||||
categoryId: number;
|
categoryId: number;
|
||||||
category?: Category;
|
category?: Category;
|
||||||
image: string;
|
image: string;
|
||||||
|
badge?: string;
|
||||||
|
rating?: number;
|
||||||
|
reviewCount?: number;
|
||||||
featured?: boolean;
|
featured?: boolean;
|
||||||
|
bestSeller?: boolean;
|
||||||
|
newArrival?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CartItem = {
|
export type CartItem = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import type { Product } from "../types";
|
import type { Product } from "../types";
|
||||||
|
import { visualStyle } from "./visuals";
|
||||||
|
|
||||||
type ProductCardProps = {
|
type ProductCardProps = {
|
||||||
product: Product;
|
product: Product;
|
||||||
@@ -9,7 +10,9 @@ type ProductCardProps = {
|
|||||||
export function ProductCard({ product, onAdd }: ProductCardProps) {
|
export function ProductCard({ product, onAdd }: ProductCardProps) {
|
||||||
return (
|
return (
|
||||||
<article className="product-card">
|
<article className="product-card">
|
||||||
<div className="product-visual" style={{ background: product.image }} />
|
<div className="product-visual" style={visualStyle(product.image)}>
|
||||||
|
{product.badge ? <span className="product-badge">{product.badge}</span> : null}
|
||||||
|
</div>
|
||||||
<div className="product-copy">
|
<div className="product-copy">
|
||||||
<div className="meta-row">
|
<div className="meta-row">
|
||||||
<span className="chip">{product.category?.name ?? "Category"}</span>
|
<span className="chip">{product.category?.name ?? "Category"}</span>
|
||||||
@@ -19,8 +22,15 @@ export function ProductCard({ product, onAdd }: ProductCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
<h3>{product.name}</h3>
|
<h3>{product.name}</h3>
|
||||||
<p>{product.description}</p>
|
<p>{product.description}</p>
|
||||||
|
<div className="rating-row">
|
||||||
|
<span>{product.rating ? `${product.rating.toFixed(1)} / 5` : "New item"}</span>
|
||||||
|
<span>{product.reviewCount ? `${product.reviewCount} reviews` : "No reviews yet"}</span>
|
||||||
|
</div>
|
||||||
<div className="product-actions">
|
<div className="product-actions">
|
||||||
|
<div className="price-stack">
|
||||||
<strong>EUR {product.price.toFixed(2)}</strong>
|
<strong>EUR {product.price.toFixed(2)}</strong>
|
||||||
|
{product.oldPrice ? <span>EUR {product.oldPrice.toFixed(2)}</span> : null}
|
||||||
|
</div>
|
||||||
<div className="inline-actions">
|
<div className="inline-actions">
|
||||||
<Link to={`/products/${product.id}`} className="ghost-button">
|
<Link to={`/products/${product.id}`} className="ghost-button">
|
||||||
Details
|
Details
|
||||||
|
|||||||
12
frontend/src/ui/visuals.ts
Normal file
12
frontend/src/ui/visuals.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export function visualStyle(image: string) {
|
||||||
|
if (image.startsWith("http")) {
|
||||||
|
return {
|
||||||
|
backgroundImage: `linear-gradient(180deg, rgba(19, 34, 56, 0.08), rgba(19, 34, 56, 0.2)), url("${image}")`,
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundRepeat: "no-repeat"
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { background: image } as const;
|
||||||
|
}
|
||||||
1
frontend/src/vite-env.d.ts
vendored
Normal file
1
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
Reference in New Issue
Block a user