This commit is contained in:
Mohamad 2025-01-13 17:52:16 +01:00
parent ca49ce7730
commit f347f80f1b
9 changed files with 426 additions and 459 deletions

View File

@ -9,10 +9,11 @@
import { authService, cartService } from '$lib/services/api';
let isOpen = false;
let isUpdating = false;
let updatingItemId: string | null = null;
let updateTimeout: NodeJS.Timeout;
let cartItems: CartItem[] = [];
let isLoading = true;
let showClearConfirmation = false;
onMount(async () => {
await loadCart();
@ -41,7 +42,7 @@
// Debounced quantity update
const handleQuantityChange = async (cartItemId: string, quantity: number) => {
clearTimeout(updateTimeout);
isUpdating = true;
updatingItemId = cartItemId;
updateTimeout = setTimeout(async () => {
try {
@ -50,10 +51,21 @@
} catch (error) {
console.error('Failed to update quantity:', error);
}
isUpdating = false;
updatingItemId = null;
}, 500);
};
// Handle increment/decrement buttons
const updateQuantity = async (cartItemId: string, delta: number) => {
const item = cartItems.find((item) => item.id === cartItemId);
if (item) {
const newQuantity = item.quantity + delta;
if (newQuantity >= 1) {
await handleQuantityChange(cartItemId, newQuantity);
}
}
};
const handleClickOutside = () => {
isOpen = false;
};
@ -79,6 +91,7 @@
// Clear entire cart
const clearEntireCart = async () => {
showClearConfirmation = false;
try {
const currentUser = authService.getCurrentUser();
if (currentUser) {
@ -95,11 +108,22 @@
isOpen = !isOpen;
goto('/main');
};
// Keyboard event handling
onMount(() => {
window.addEventListener('keydown', handleKeyDown);
});
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
isOpen = false;
}
};
</script>
<!-- Cart Icon Button with Badge -->
<div class="relative">
<Button on:click={() => (isOpen = !isOpen)} class="relative h-12">
<Button on:click={() => (isOpen = !isOpen)} class="relative h-12" aria-label="Open Cart">
<CartOutline size="lg" />
{#if cartItems.length > 0}
<span
@ -124,23 +148,32 @@
{#if isLoading}
<p class="py-4 text-center text-gray-500">Loading cart...</p>
{:else if cartItems.length === 0}
<p class="py-4 text-center text-gray-500">Your cart is empty</p>
<div class="flex flex-col items-center py-6">
<img src="/empty-cart.svg" alt="Empty Cart" class="mb-4 h-32 w-32" />
<p class="mb-4 text-center text-gray-500">Your cart is empty</p>
<Button size="xl" on:click={goToShop}>
Shop Now <ArrowRightOutline class="ml-2 h-5 w-5" />
</Button>
</div>
{:else}
<ul class="max-h-96 space-y-3 overflow-y-auto">
<ul class="max-h-96 space-y-3 overflow-x-hidden overflow-y-scroll">
{#each cartItems as item (item.id)}
<li
id="cart-item-{item.id}"
class="flex items-center justify-between rounded-lg bg-gray-50 p-3 transition-all duration-200 hover:bg-gray-100"
class=" flex items-center justify-between rounded-lg bg-gray-50 p-3 transition-all duration-200 hover:scale-105 hover:bg-gray-100"
>
<div class="flex-grow">
<h3 class="font-semibold">{item.expand?.product?.title}</h3>
<p class="text-sm text-gray-600">${item.expand?.product?.price.toFixed(2)} each</p>
</div>
<div class="flex items-center gap-3">
<div class="relative">
<div class="flex items-center gap-2">
<button
on:click={() => updateQuantity(item.id, -1)}
class="rounded bg-gray-200 p-1 hover:bg-gray-300"
>
</button>
<input
type="number"
value={item.quantity}
@ -148,11 +181,12 @@
on:input={(e) => handleQuantityChange(item.id, parseInt(e.currentTarget.value))}
class="w-16 rounded border p-1 text-center"
/>
{#if isUpdating}
<span
class="absolute -top-1 right-0 h-2 w-2 animate-pulse rounded-full bg-blue-500"
></span>
{/if}
<button
on:click={() => updateQuantity(item.id, 1)}
class="rounded bg-gray-200 p-1 hover:bg-gray-300"
>
</button>
</div>
<button
on:click={() => removeItem(item.id)}
@ -173,13 +207,32 @@
</div>
<div class="mt-4 space-y-2">
<Button href="/checkout" on:click={() => (isOpen = !isOpen)} class="w-full"
>Proceed to Checkout</Button
<Button href="/checkout" on:click={() => (isOpen = !isOpen)} class="w-full">
Proceed to Checkout
</Button>
<Button
on:click={() => (showClearConfirmation = true)}
color="red"
class="w-full"
variant="outline"
>
<Button on:click={clearEntireCart} color="red" class="w-full" variant="outline">
Clear Cart
</Button>
</div>
{/if}
</div>
{/if}
<!-- Confirmation Dialog for Clearing Cart -->
{#if showClearConfirmation}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div class="rounded-lg bg-white p-6 shadow-lg">
<h3 class="mb-4 text-lg font-bold">Are you sure?</h3>
<p class="mb-6">This will remove all items from your cart.</p>
<div class="flex justify-end gap-3">
<Button on:click={() => (showClearConfirmation = false)} variant="outline">Cancel</Button>
<Button on:click={clearEntireCart} color="red">Clear Cart</Button>
</div>
</div>
</div>
{/if}

View File

@ -178,6 +178,19 @@ export const favoritesService = {
} catch (error) {
throw handleError(error);
}
},
clearFavorites: async (userId: string): Promise<void> => {
try {
const favorites = await pb.collection('favorites').getFullList({
filter: `user = "${userId}"`
});
for (const favorite of favorites) {
await pb.collection('favorites').delete(favorite.id);
}
} catch (error) {
throw handleError(error);
}
}
};

View File

@ -24,8 +24,8 @@ export interface CartItem {
user: string; // User ID
product: string; // Product ID
quantity: number;
created: string;
updated: string;
created?: string;
updated?: string;
expand?: {
product: Product; // Expanded product details
};
@ -39,7 +39,7 @@ export interface Favorite {
created: string;
updated: string;
expand?: {
product: Product; // Expanded product details
product?: Product[]; // Expanded product details
};
}

View File

@ -1,21 +0,0 @@
const USER_KEY = 'user';
const PASS_KEY = 'username';
export const login = (user: string, password: string) => {
localStorage.setItem(USER_KEY, user);
localStorage.setItem(PASS_KEY, password);
};
export const logout = () => {
localStorage.removeItem(USER_KEY);
localStorage.removeItem(PASS_KEY);
window.location.reload();
};
export const loggedIn = () => localStorage.getItem(USER_KEY) !== null;
export const name = () => localStorage.getItem(PASS_KEY) ?? '';
export const auth = () => localStorage.getItem(USER_KEY);
export default { login, logout, loggedIn, name, auth };

View File

@ -1,47 +0,0 @@
import { writable } from 'svelte/store';
// Define the type for a product
export interface Product {
id: number;
title: string;
description: string;
imageUrl: string;
price: number;
quantity: number;
}
// Initialize the cart store
export const cart = writable<Product[]>([]);
// Add a product to the cart
export const addToCart = (product: Product) => {
cart.update((items) => {
const existingItem = items.find((item) => item.id === product.id);
if (existingItem) {
// If the product already exists, increase the quantity
return items.map((item) =>
item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item
);
} else {
// If the product doesn't exist, add it to the cart
return [...items, { ...product, quantity: 1 }];
}
});
};
// Remove a product from the cart
export const removeFromCart = (productId: number) => {
cart.update((items) => items.filter((item) => item.id !== productId));
};
// Update the quantity of a product in the cart
export const updateQuantity = (productId: number, quantity: number) => {
cart.update((items) =>
items.map((item) => (item.id === productId ? { ...item, quantity } : item))
);
};
// Clear the cart
export const clearCart = () => {
cart.set([]);
};

View File

@ -1,86 +0,0 @@
import { writable } from 'svelte/store';
// Define the type for a product
export interface Product {
id: number;
title: string;
description: string;
imageUrl: string;
price: number; // Added price for cart calculations
quantity?: number; // Optional quantity for cart items
}
// Define the type for the cart and favorites
export interface AppState {
cart: Product[];
favorites: Product[];
}
// Initialize the store
const initialState: AppState = {
cart: [],
favorites: []
};
export const appStore = writable<AppState>(initialState);
export const favorites = writable<Product[]>([]);
// Add a product to the cart
export const addToCart = (product: Product) => {
appStore.update((state) => {
const existingItem = state.cart.find((item) => item.id === product.id);
if (existingItem) {
// If the product already exists, increase the quantity
return {
...state,
cart: state.cart.map((item) =>
item.id === product.id ? { ...item, quantity: (item.quantity || 1) + 1 } : item
)
};
} else {
// If the product doesn't exist, add it to the cart with quantity 1
return { ...state, cart: [...state.cart, { ...product, quantity: 1 }] };
}
});
};
// Remove a product from the cart
export const removeFromCart = (productId: number) => {
appStore.update((state) => ({
...state,
cart: state.cart.filter((item) => item.id !== productId)
}));
};
// Update the quantity of a product in the cart
export const updateCartQuantity = (productId: number, quantity: number) => {
appStore.update((state) => ({
...state,
cart: state.cart.map((item) => (item.id === productId ? { ...item, quantity } : item))
}));
};
// Add a product to favorites
export const addToFavorites = (product: Product) => {
favorites.update((items) => {
if (!items.some((item) => item.id === product.id)) {
return [...items, product];
}
return items;
});
};
// Remove a product from favorites
export const removeFromFavorites = (productId: number) => {
favorites.update((items) => items.filter((item) => item.id !== productId));
};
// Clear the cart
export const clearCart = () => {
appStore.update((state) => ({ ...state, cart: [] }));
};
// Clear favorites
export const clearFavorites = () => {
favorites.update((state) => ({ ...state, favorites: [] }));
};

View File

@ -1,193 +1,170 @@
<script lang="ts">
import { onMount } from 'svelte';
import { cartService, orderService, authService } from '$lib/services/api';
import { Button, Input, Card, Badge } from 'flowbite-svelte';
import { ArrowRightOutline, CheckCircleOutline } from 'flowbite-svelte-icons';
import type { CartItem } from '$lib/services/types';
import { goto } from '$app/navigation';
import { clearCart, cart, type Product } from '$lib/stores/cartStore';
import { Card, Input, Button, Alert } from 'flowbite-svelte';
import { fade } from 'svelte/transition';
// Form state
let formData = {
name: '',
email: '',
address: '',
paymentMethod: 'credit_card'
};
// State variables
let cartItems: CartItem[] = [];
let total = 0;
let name = '';
let email = '';
let address = '';
let paymentMethod = 'credit_card';
let isLoading = false;
let isProcessing = false;
let error = '';
let success = '';
// Calculate the total price
$: total = $cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
// Form validation
$: isFormValid =
formData.name.length > 0 &&
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email) &&
formData.address.length > 0;
// Handle form submission
const handleCheckout = async () => {
if (!isFormValid) {
error = 'Please fill in all required fields correctly.';
// Fetch the user's cart on mount
onMount(async () => {
const user = authService.getCurrentUser();
if (!user) {
goto('/login'); // Redirect to login if not authenticated
return;
}
isProcessing = true;
error = '';
try {
cartItems = await cartService.getCart(user.id);
total = cartItems.reduce(
(sum, item) => sum + (item.expand?.product.price || 0) * item.quantity,
0
);
} catch (error) {
console.error('Failed to fetch cart:', error);
}
});
const checkoutRequest = {
...formData,
items: $cart
};
// Handle form submission
const handleCheckout = async () => {
const user = authService.getCurrentUser();
if (!user) {
goto('/login'); // Redirect to login if not authenticated
return;
}
isLoading = true;
try {
clearCart();
// Prepare order items
const items = cartItems.map((item) => ({
product: item.product,
quantity: item.quantity
}));
goto(`/order-confirmation/12345`);
} catch (err) {
error = 'Failed to process checkout. Please try again.';
// Create the order
const order = await orderService.createOrder(user.id, items, total);
// Clear the cart
await cartService.clearCart(user.id);
// Redirect to order confirmation page
goto(`/order-confirmation/${order.id}`);
} catch (error) {
console.error('Failed to place order:', error);
alert('Failed to place order. Please try again.');
} finally {
isProcessing = false;
isLoading = false;
}
};
let modalOpen = false;
let selectedImage = {
url: '',
title: ''
};
const openImageModal = (product: Product) => {
selectedImage = {
url: product.imageUrl,
title: product.title
};
modalOpen = true;
};
const closeImageModal = () => {
modalOpen = false;
};
onMount(() => {
const elements = document.querySelectorAll('.fade-in');
elements.forEach((el) => {
el.classList.add('visible');
});
});
</script>
<main class="mx-auto max-w-4xl p-6">
<h1 class="mb-6 text-3xl font-bold">Checkout</h1>
<main class="container mx-auto px-4 py-8">
<!-- Hero Section -->
<section class="mb-8 rounded-lg bg-gradient-to-r from-pink-100 to-purple-100 py-20">
<div class="container mx-auto px-4 text-center">
<h1 class="mb-4 text-5xl font-bold text-gray-900">Checkout</h1>
<p class="mb-8 text-xl text-gray-600">
Review your order and enter your details to complete the purchase.
</p>
</div>
</section>
{#if $cart.length === 0}
<Alert color="red" class="mb-6">
Your cart is empty. Please add items before checking out.
</Alert>
{:else}
<div class="flex gap-10">
<!-- Order Summary -->
<!-- Cart Summary -->
<Card class="mb-6">
<h2 class="mb-4 text-xl font-semibold">Order Summary</h2>
<ul class="space-y-3">
{#each $cart as item}
<li class="flex items-center justify-between rounded-lg bg-gray-50 p-3">
<div class="flex">
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<img
src={item.imageUrl}
alt="item"
class="me-2 h-20 w-20"
on:click={() => openImageModal(item)}
on:keydown={(e) => e.key === 'Enter' && openImageModal(item)}
tabindex="0"
/>
<h2 class="mb-4 text-2xl font-bold">Order Summary</h2>
<ul class="space-y-4">
{#each cartItems as item}
<li class="flex items-center justify-between border-b pb-4">
<div>
<h3 class="font-semibold">{item.title}</h3>
<p class="text-sm text-gray-600">Quantity: {item.quantity}</p>
<h3 class="text-xl font-semibold text-gray-900">{item.expand?.product.title}</h3>
<p class="text-gray-600">x{item.quantity}</p>
</div>
</div>
<span class="text-lg font-medium">
${(item.price * item.quantity).toFixed(2)}
</span>
<p class="text-2xl font-bold text-gray-900">
${((item.expand?.product.price || 0) * item.quantity).toFixed(2)}
</p>
</li>
{/each}
</ul>
<div class="mt-4 border-t pt-4">
<div class="flex justify-between text-lg">
<strong>Total:</strong>
<span class="font-bold">${total.toFixed(2)}</span>
</div>
<div class="mt-6 border-t pt-4">
<p class="text-2xl font-bold text-gray-900">Total: ${total.toFixed(2)}</p>
</div>
</Card>
<!-- Customer Details Form -->
<Card class="mb-6">
<h2 class="mb-4 text-xl font-semibold">Customer Details</h2>
<h2 class="mb-4 text-2xl font-bold">Customer Details</h2>
<form on:submit|preventDefault={handleCheckout} class="space-y-4">
<Input
label="Full Name"
bind:value={formData.name}
placeholder="John Doe"
required
error={!formData.name && 'Name is required'}
/>
<Input label="Full Name" bind:value={name} placeholder="John Doe" required />
<Input
label="Email"
type="email"
bind:value={formData.email}
bind:value={email}
placeholder="john@example.com"
required
error={formData.email &&
!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email) &&
'Please enter a valid email'}
/>
<Input
label="Shipping Address"
bind:value={formData.address}
bind:value={address}
placeholder="123 Main St, City, Country"
required
error={!formData.address && 'Address is required'}
/>
<div>
<label class="mb-2 block font-medium">Payment Method</label>
<div class="space-y-2">
{#each [{ value: 'credit_card', label: 'Credit Card', icon: '💳' }, { value: 'paypal', label: 'PayPal', icon: '🅿️' }, { value: 'cash_on_delivery', label: 'Cash on Delivery', icon: '💵' }] as method}
<label
class="flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors hover:bg-gray-50"
>
<input
type="radio"
bind:group={formData.paymentMethod}
value={method.value}
class="h-4 w-4"
<Input
label="Payment Method"
bind:value={paymentMethod}
options={[
{ value: 'credit_card', name: 'Credit Card' },
{ value: 'paypal', name: 'PayPal' },
{ value: 'cash_on_delivery', name: 'Cash on Delivery' }
]}
required
/>
<span>{method.icon}</span>
<span>{method.label}</span>
</label>
{/each}
</div>
</div>
{#if error}
<Alert color="red">
{error}
</Alert>
{/if}
{#if success}
<Alert color="green">
{success}
</Alert>
{/if}
<Button type="submit" color="blue" class="w-full" disabled={isProcessing || !isFormValid}>
{#if isProcessing}
<span class="inline-block animate-spin"></span>
Processing...
{:else}
Place Order (${total.toFixed(2)})
{/if}
<Button type="submit" color="blue" class="w-full" disabled={isLoading}>
{isLoading ? 'Placing Order...' : 'Place Order'}
<ArrowRightOutline class="ml-2 h-5 w-5" />
</Button>
</form>
</Card>
</div>
{/if}
<!-- Guarantee Section -->
<section class="rounded-lg bg-gradient-to-r from-pink-100 to-purple-100 py-12 text-center">
<CheckCircleOutline class="mx-auto mb-4 h-12 w-12 text-purple-500" />
<h2 class="mb-4 text-2xl font-bold text-gray-900">Satisfaction Guaranteed</h2>
<p class="text-gray-600">
We stand by our products with a 100% satisfaction guarantee. If you're not happy, we'll make
it right.
</p>
</section>
</main>
<style>
/* Add custom animations */
.fade-in {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.6s ease-out,
transform 0.6s ease-out;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
</style>

View File

@ -1,33 +1,85 @@
<script lang="ts">
import { favorites, removeFromFavorites, clearFavorites } from '$lib/stores/store';
import { onMount } from 'svelte';
import { favoritesService, authService } from '$lib/services/api';
import { Button, Card } from 'flowbite-svelte';
import { TrashBinOutline } from 'flowbite-svelte-icons';
import { goto } from '$app/navigation';
import type { Favorite } from '$lib/services/types';
let favorites: Favorite[] = [];
let isLoading = true;
const fetchFavorites = async () => {
const user = authService.getCurrentUser();
if (!user) {
goto('/login');
return;
}
try {
const fetchedFavorites = await favoritesService.getFavorites(user.id);
favorites = fetchedFavorites.map((favorite: Favorite) => ({
...favorite,
expand: favorite.expand ?? { product: [] }
}));
} catch (error) {
console.error('Failed to fetch favorites:', error);
} finally {
isLoading = false;
}
};
const removeFromFavorites = async (favoriteId: string) => {
try {
await favoritesService.removeFromFavorites(favoriteId);
favorites = favorites.filter((favorite) => favorite.id !== favoriteId);
} catch (error) {
console.error('Failed to remove favorite:', error);
}
};
onMount(() => {
fetchFavorites();
});
</script>
<main class="p-6">
<h1 class="mb-6 text-3xl font-bold">Favorites</h1>
{#if $favorites.length > 0}
<ul class="space-y-4">
{#each $favorites as item}
<li class="flex items-center justify-between border-b pb-4">
<div>
<h2 class="text-xl font-semibold">{item.title}</h2>
<p class="text-gray-600">{item.description}</p>
</div>
<button
on:click={() => removeFromFavorites(item.id)}
class="text-red-500 hover:text-red-700"
>
🗑️
</button>
</li>
{/each}
</ul>
<button
on:click={clearFavorites}
class="mt-4 w-full rounded bg-red-500 p-2 text-white hover:bg-red-600"
>
Clear Favorites
</button>
<main class="container mx-auto px-4 py-8">
<h1 class="mb-8 text-4xl font-bold text-gray-900">Your Favorites</h1>
{#if isLoading}
<p class="text-gray-600">Loading your favorites...</p>
{:else if favorites.length === 0}
<p class="text-gray-600">You have no favorite products yet.</p>
{:else}
<p class="text-gray-600">You have no favorite products.</p>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{#each favorites as favorite}
<Card class="h-full">
<div class="flex h-full flex-col">
<img
src={favorite.expand?.product?.[0]?.imageUrl || '/fallback-image.jpg'}
alt={favorite.expand?.product?.[0]?.title || 'Product image'}
class="h-48 w-full rounded-t-lg object-cover"
/>
<div class="flex-grow p-4">
<h2 class="mb-2 text-xl font-bold text-gray-900">
{favorite.expand?.product?.[0]?.title || 'Unnamed Product'}
</h2>
<p class="mb-4 text-gray-600">
{favorite.expand?.product?.[0]?.description || 'No description available.'}
</p>
<p class="text-2xl font-bold text-gray-900">
${favorite.expand?.product?.[0]?.price || 'N/A'}
</p>
</div>
<div class="p-4">
<Button color="red" class="w-full" on:click={() => removeFromFavorites(favorite.id)}>
<TrashBinOutline class="mr-2 h-5 w-5" />
Remove from Favorites
</Button>
</div>
</div>
</Card>
{/each}
</div>
{/if}
</main>

View File

@ -1,53 +1,74 @@
<script lang="ts">
import { favorites } from '$lib/stores/store';
import { authService, orderService } from '$lib/services/api';
import { Card, Button, Badge, Input, Textarea } from 'flowbite-svelte';
import { StarSolid, CheckCircleOutline, PenOutline } from 'flowbite-svelte-icons';
import { onMount } from 'svelte';
// Example user data
const user = {
name: 'John Doe',
email: 'john.doe@example.com',
address: '123 Main St, City, Country'
};
// User data
let user: { id: string; name: string; email: string; address?: string } | null = null;
// Example order history
const orders = [
{
id: 1,
date: '2023-10-01',
items: ['Tulip Bouquet', 'Rose Bouquet'],
total: 60,
status: 'Delivered'
},
{
id: 2,
date: '2023-09-25',
items: ['Sunflower Bouquet'],
total: 30,
status: 'Shipped'
}
];
// Order history
let orders: Array<{
id: string;
created: string;
items: Array<{ product: string; quantity: number }>;
total: number;
status: string;
}> = [];
// State for editing profile
let isEditing = false;
let name = user.name;
let email = user.email;
let address = user.address;
let name = '';
let email = '';
let address = '';
// Fetch user and order data on component mount
onMount(async () => {
// Get the current user
user = authService.getCurrentUser();
if (user) {
name = user.name;
email = user.email;
address = user.address || '';
// Fetch the user's orders
try {
const userOrders = await orderService.getOrders(user.id);
orders = userOrders.map((order) => ({
id: order.id,
created: order.created,
items: order.items,
total: order.total,
status: order.status
}));
} catch (error) {
console.error('Failed to fetch orders:', error);
}
}
});
// Handle profile update
const updateProfile = () => {
const updateProfile = async () => {
if (user) {
try {
// Update the user's profile (this is a placeholder; you'll need to implement the API call)
// Example: await authService.updateProfile(user.id, { name, email, address });
user.name = name;
user.email = email;
user.address = address;
isEditing = false;
// You could add an API call here to update the user's profile
console.log('Profile updated successfully');
} catch (error) {
console.error('Failed to update profile:', error);
}
}
};
</script>
<!-- Welcome Section -->
<section class="bg-gradient-to-r from-pink-100 to-purple-100 py-12">
<div class="container mx-auto px-4">
<h1 class="text-4xl font-bold text-gray-900">Welcome, {user.name}!</h1>
<h1 class="text-4xl font-bold text-gray-900">Welcome, {user?.name || user?.email}!</h1>
<p class="text-lg text-gray-600">Manage your orders, favorites, and account settings here.</p>
</div>
</section>
@ -58,34 +79,40 @@
<div class="lg:col-span-2">
<h2 class="mb-6 text-2xl font-bold text-gray-900">Order History</h2>
<div class="space-y-4">
{#if orders.length > 0}
{#each orders as order}
<Card class="p-6">
<div class="mb-4 flex items-center justify-between">
<h3 class="text-xl font-bold text-gray-900">Order #{order.id}</h3>
<Badge color={order.status === 'Delivered' ? 'green' : 'blue'}>
<Badge color={order.status === 'delivered' ? 'green' : 'blue'}>
{order.status}
</Badge>
</div>
<p class="mb-2 text-gray-600">
<strong>Date:</strong>
{order.date}
{new Date(order.created).toLocaleDateString()}
</p>
<p class="mb-2 text-gray-600">
<strong>Items:</strong>
{order.items.join(', ')}
{#each order.items as item}
{item.product} (x{item.quantity}){@const isLast =
item === order.items[order.items.length - 1]}{#if !isLast},
{/if}
{/each}
</p>
<p class="text-gray-600">
<strong>Total:</strong> ${order.total.toFixed(2)}
</p>
</Card>
{/each}
{:else}
<p class="text-gray-600">No orders found.</p>
{/if}
</div>
</div>
<!-- Account Settings -->
<div class="space-y-8">
<!-- Account Settings -->
<div>
<h2 class="mb-6 text-2xl font-bold text-gray-900">Account Settings</h2>
<Card class="p-6">
{#if isEditing}
@ -100,9 +127,9 @@
</form>
{:else}
<div class="space-y-4">
<p class="text-gray-600"><strong>Name:</strong> {user.name}</p>
<p class="text-gray-600"><strong>Email:</strong> {user.email}</p>
<p class="text-gray-600"><strong>Address:</strong> {user.address}</p>
<p class="text-gray-600"><strong>Name:</strong> {user?.name}</p>
<p class="text-gray-600"><strong>Email:</strong> {user?.email}</p>
<p class="text-gray-600"><strong>Address:</strong> {user?.address || 'Not provided'}</p>
<Button on:click={() => (isEditing = true)}>
<PenOutline class="mr-2 h-5 w-5" />
Edit Profile
@ -111,5 +138,4 @@
{/if}
</Card>
</div>
</div>
</div>