ruch
Version:
Revolutionary React TypeScript CLI with hexagonal architecture & AI-powered development assistance. Create maintainable, scalable applications with domain-driven design and integrated AI tooling.
376 lines (342 loc) • 11.3 kB
text/typescript
/**
* Product Domain Hooks
*
* React hooks for product-related operations following the hexagonal architecture pattern.
* These hooks integrate with React Query for efficient data fetching and caching.
*/
import { useState, useEffect } from 'react';
import type {
Product,
ProductCategory,
ProductFilter,
ProductSort
} from '../entities';
// Mock data pour les produits
const mockProducts: Product[] = [
{
id: '1',
name: 'Premium Wireless Headphones',
description: 'High-quality wireless headphones with noise cancellation and premium sound quality.',
price: 299.99,
currency: 'USD',
brand: 'TechBrand',
sku: 'WH-1000XM4',
category: { id: 'electronics', name: 'Electronics', slug: 'electronics' },
images: [
{
id: '1',
url: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600&h=400&fit=crop',
alt: 'Premium Wireless Headphones',
isPrimary: true,
order: 1
}
],
inventory: { isInStock: true, quantity: 50, lowStockThreshold: 10, reserved: 5, available: 45 },
specifications: [
{ name: 'Battery Life', value: '30', unit: 'hours' },
{ name: 'Weight', value: '254', unit: 'g' }
],
isFeatured: true,
isActive: true,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z'
},
{
id: '2',
name: 'Smart Watch Pro',
description: 'Advanced smartwatch with health monitoring, GPS, and long battery life.',
price: 399.99,
currency: 'USD',
brand: 'TechBrand',
sku: 'SW-PRO-2024',
category: { id: 'wearables', name: 'Wearables', slug: 'wearables' },
images: [
{
id: '2',
url: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=600&h=400&fit=crop',
alt: 'Smart Watch Pro',
isPrimary: true,
order: 1
}
],
inventory: { isInStock: true, quantity: 30, lowStockThreshold: 5, reserved: 2, available: 28 },
specifications: [
{ name: 'Battery Life', value: '7', unit: 'days' },
{ name: 'Display', value: '1.4', unit: 'inches' }
],
isFeatured: true,
isActive: true,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z'
},
{
id: '3',
name: 'Wireless Gaming Mouse',
description: 'High-precision wireless gaming mouse with customizable RGB lighting.',
price: 89.99,
currency: 'USD',
brand: 'GameGear',
sku: 'GM-WIRELESS-X1',
category: { id: 'gaming', name: 'Gaming', slug: 'gaming' },
images: [
{
id: '3',
url: 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=600&h=400&fit=crop',
alt: 'Wireless Gaming Mouse',
isPrimary: true,
order: 1
}
],
inventory: { isInStock: true, quantity: 75, lowStockThreshold: 15, reserved: 8, available: 67 },
specifications: [
{ name: 'DPI', value: '16000', unit: '' },
{ name: 'Battery Life', value: '80', unit: 'hours' }
],
isFeatured: false,
isActive: true,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z'
},
{
id: '4',
name: 'Ultra-Wide Monitor',
description: '34-inch ultra-wide monitor perfect for productivity and gaming.',
price: 799.99,
currency: 'USD',
brand: 'DisplayTech',
sku: 'UW-34-4K',
category: { id: 'monitors', name: 'Monitors', slug: 'monitors' },
images: [
{
id: '4',
url: 'https://images.unsplash.com/photo-1593640408182-31c70c8268f5?w=600&h=400&fit=crop',
alt: 'Ultra-Wide Monitor',
isPrimary: true,
order: 1
}
],
inventory: { isInStock: false, quantity: 0, lowStockThreshold: 3, reserved: 0, available: 0 },
specifications: [
{ name: 'Size', value: '34', unit: 'inches' },
{ name: 'Resolution', value: '3440x1440', unit: '' }
],
isFeatured: false,
isActive: true,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z'
}
];
const mockCategories: ProductCategory[] = [
{ id: 'electronics', name: 'Electronics', slug: 'electronics' },
{ id: 'wearables', name: 'Wearables', slug: 'wearables' },
{ id: 'gaming', name: 'Gaming', slug: 'gaming' },
{ id: 'monitors', name: 'Monitors', slug: 'monitors' }
];
/**
* Hook pour récupérer tous les produits avec filtres et tri optionnels
*/
export function useProducts(filters?: ProductFilter, sort?: ProductSort) {
const [data, setData] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
setIsLoading(true);
// Simuler un délai API
setTimeout(() => {
try {
let filteredProducts = [...mockProducts];
// Appliquer les filtres
if (filters) {
if (filters.categoryId) {
filteredProducts = filteredProducts.filter(p => p.category.id === filters.categoryId);
}
if (filters.minPrice !== undefined) {
filteredProducts = filteredProducts.filter(p => p.price >= filters.minPrice!);
}
if (filters.maxPrice !== undefined) {
filteredProducts = filteredProducts.filter(p => p.price <= filters.maxPrice!);
}
if (filters.inStock !== undefined) {
filteredProducts = filteredProducts.filter(p => p.inventory.isInStock === filters.inStock);
}
if (filters.isFeatured !== undefined) {
filteredProducts = filteredProducts.filter(p => p.isFeatured === filters.isFeatured);
}
if (filters.brand) {
filteredProducts = filteredProducts.filter(p => p.brand === filters.brand);
}
if (filters.search) {
const query = filters.search.toLowerCase();
filteredProducts = filteredProducts.filter(p =>
p.name.toLowerCase().includes(query) ||
p.description.toLowerCase().includes(query)
);
}
}
// Appliquer le tri
if (sort) {
filteredProducts.sort((a, b) => {
switch (sort.field) {
case 'price':
return sort.direction === 'asc' ? a.price - b.price : b.price - a.price;
case 'name':
return sort.direction === 'asc'
? a.name.localeCompare(b.name)
: b.name.localeCompare(a.name);
case 'createdAt':
return sort.direction === 'asc'
? new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
: new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
default:
return 0;
}
});
}
setData(filteredProducts);
setError(null);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setIsLoading(false);
}
}, 500);
}, [filters, sort]);
return { data, isLoading, error };
}
/**
* Hook pour récupérer un produit par ID
*/
export function useProduct(id: string) {
const [data, setData] = useState<Product | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
try {
const product = mockProducts.find(p => p.id === id);
setData(product || null);
setError(product ? null : new Error('Product not found'));
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setIsLoading(false);
}
}, 300);
}, [id]);
return { data, isLoading, error };
}
/**
* Hook pour récupérer les produits en vedette
*/
export function useFeaturedProducts() {
const [data, setData] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
try {
const featuredProducts = mockProducts.filter(p => p.isFeatured);
setData(featuredProducts);
setError(null);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setIsLoading(false);
}
}, 300);
}, []);
return { data, isLoading, error };
}
/**
* Hook pour récupérer les catégories de produits
*/
export function useProductCategories() {
const [data, setData] = useState<ProductCategory[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
try {
setData(mockCategories);
setError(null);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setIsLoading(false);
}
}, 200);
}, []);
return { data, isLoading, error };
}
/**
* Hook pour créer un produit
*/
export function useCreateProduct() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const mutate = async (productData: Partial<Product>) => {
setIsLoading(true);
setError(null);
try {
// Simuler création
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Creating product:', productData);
// En production, ceci ferait un appel API
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to create product'));
throw err;
} finally {
setIsLoading(false);
}
};
return { mutate, isLoading, error };
}
/**
* Hook pour mettre à jour un produit
*/
export function useUpdateProduct() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const mutate = async ({ id, ...productData }: { id: string } & Partial<Product>) => {
setIsLoading(true);
setError(null);
try {
// Simuler mise à jour
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Updating product:', id, productData);
// En production, ceci ferait un appel API
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to update product'));
throw err;
} finally {
setIsLoading(false);
}
};
return { mutate, isLoading, error };
}
/**
* Hook pour supprimer un produit
*/
export function useDeleteProduct() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const mutate = async (id: string) => {
setIsLoading(true);
setError(null);
try {
// Simuler suppression
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Deleting product:', id);
// En production, ceci ferait un appel API
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to delete product'));
throw err;
} finally {
setIsLoading(false);
}
};
return { mutate, isLoading, error };
}