380 lines
11 KiB
TypeScript
380 lines
11 KiB
TypeScript
|
|
import { Injectable } from '@angular/core';
|
||
|
|
import { Observable, of, delay } from 'rxjs';
|
||
|
|
import { Project, Category, Subcategory, Item, ItemsListResponse } from '../models';
|
||
|
|
|
||
|
|
@Injectable({
|
||
|
|
providedIn: 'root'
|
||
|
|
})
|
||
|
|
export class MockDataService {
|
||
|
|
private projects: Project[] = [
|
||
|
|
{
|
||
|
|
id: 'dexar',
|
||
|
|
name: 'dexar',
|
||
|
|
displayName: 'Dexar Marketplace',
|
||
|
|
active: true,
|
||
|
|
logoUrl: 'https://via.placeholder.com/150?text=Dexar'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'novo',
|
||
|
|
name: 'novo',
|
||
|
|
displayName: 'Novo Shop',
|
||
|
|
active: true,
|
||
|
|
logoUrl: 'https://via.placeholder.com/150?text=Novo'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
private categories: Category[] = [
|
||
|
|
{
|
||
|
|
id: 'cat1',
|
||
|
|
name: 'Electronics',
|
||
|
|
visible: true,
|
||
|
|
priority: 1,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Electronics',
|
||
|
|
projectId: 'dexar',
|
||
|
|
subcategories: [
|
||
|
|
{
|
||
|
|
id: 'sub1',
|
||
|
|
name: 'Smartphones',
|
||
|
|
visible: true,
|
||
|
|
priority: 1,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Smartphones',
|
||
|
|
categoryId: 'cat1',
|
||
|
|
itemCount: 15
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'sub2',
|
||
|
|
name: 'Laptops',
|
||
|
|
visible: true,
|
||
|
|
priority: 2,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Laptops',
|
||
|
|
categoryId: 'cat1',
|
||
|
|
itemCount: 12
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'cat2',
|
||
|
|
name: 'Clothing',
|
||
|
|
visible: true,
|
||
|
|
priority: 2,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Clothing',
|
||
|
|
projectId: 'dexar',
|
||
|
|
subcategories: [
|
||
|
|
{
|
||
|
|
id: 'sub3',
|
||
|
|
name: 'Men',
|
||
|
|
visible: true,
|
||
|
|
priority: 1,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Men',
|
||
|
|
categoryId: 'cat2',
|
||
|
|
itemCount: 25
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'cat3',
|
||
|
|
name: 'Home & Garden',
|
||
|
|
visible: false,
|
||
|
|
priority: 3,
|
||
|
|
img: 'https://via.placeholder.com/400x300?text=Home',
|
||
|
|
projectId: 'novo',
|
||
|
|
subcategories: []
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
private items: Item[] = [
|
||
|
|
{
|
||
|
|
id: 'item1',
|
||
|
|
name: 'iPhone 15 Pro Max',
|
||
|
|
visible: true,
|
||
|
|
priority: 1,
|
||
|
|
quantity: 50,
|
||
|
|
price: 1299,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: [
|
||
|
|
'https://via.placeholder.com/600x400?text=iPhone+Front',
|
||
|
|
'https://via.placeholder.com/600x400?text=iPhone+Back'
|
||
|
|
],
|
||
|
|
tags: ['new', 'featured', 'bestseller'],
|
||
|
|
simpleDescription: 'Latest iPhone with titanium design and A17 Pro chip',
|
||
|
|
description: [
|
||
|
|
{ key: 'Color', value: 'Natural Titanium' },
|
||
|
|
{ key: 'Storage', value: '256GB' },
|
||
|
|
{ key: 'Display', value: '6.7 inch Super Retina XDR' },
|
||
|
|
{ key: 'Chip', value: 'A17 Pro' }
|
||
|
|
],
|
||
|
|
subcategoryId: 'sub1',
|
||
|
|
comments: [
|
||
|
|
{
|
||
|
|
id: 'c1',
|
||
|
|
text: 'Great phone! Battery life is amazing.',
|
||
|
|
createdAt: new Date('2024-01-10'),
|
||
|
|
author: 'John Doe'
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'item2',
|
||
|
|
name: 'Samsung Galaxy S24 Ultra',
|
||
|
|
visible: true,
|
||
|
|
priority: 2,
|
||
|
|
quantity: 35,
|
||
|
|
price: 1199,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: ['https://via.placeholder.com/600x400?text=Samsung+S24'],
|
||
|
|
tags: ['new', 'android'],
|
||
|
|
simpleDescription: 'Premium Samsung flagship with S Pen',
|
||
|
|
description: [
|
||
|
|
{ key: 'Color', value: 'Titanium Gray' },
|
||
|
|
{ key: 'Storage', value: '512GB' },
|
||
|
|
{ key: 'RAM', value: '12GB' }
|
||
|
|
],
|
||
|
|
subcategoryId: 'sub1'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'item3',
|
||
|
|
name: 'Google Pixel 8 Pro',
|
||
|
|
visible: true,
|
||
|
|
priority: 3,
|
||
|
|
quantity: 20,
|
||
|
|
price: 999,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: ['https://via.placeholder.com/600x400?text=Pixel+8'],
|
||
|
|
tags: ['sale', 'android', 'ai'],
|
||
|
|
simpleDescription: 'Best AI photography phone',
|
||
|
|
description: [
|
||
|
|
{ key: 'Color', value: 'Bay Blue' },
|
||
|
|
{ key: 'Storage', value: '256GB' }
|
||
|
|
],
|
||
|
|
subcategoryId: 'sub1'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'item4',
|
||
|
|
name: 'MacBook Pro 16"',
|
||
|
|
visible: true,
|
||
|
|
priority: 1,
|
||
|
|
quantity: 15,
|
||
|
|
price: 2499,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: ['https://via.placeholder.com/600x400?text=MacBook'],
|
||
|
|
tags: ['featured', 'professional'],
|
||
|
|
simpleDescription: 'Powerful laptop for professionals',
|
||
|
|
description: [
|
||
|
|
{ key: 'Processor', value: 'M3 Max' },
|
||
|
|
{ key: 'RAM', value: '36GB' },
|
||
|
|
{ key: 'Storage', value: '1TB SSD' }
|
||
|
|
],
|
||
|
|
subcategoryId: 'sub2'
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 'item5',
|
||
|
|
name: 'Dell XPS 15',
|
||
|
|
visible: true,
|
||
|
|
priority: 2,
|
||
|
|
quantity: 0,
|
||
|
|
price: 1799,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: ['https://via.placeholder.com/600x400?text=Dell+XPS'],
|
||
|
|
tags: ['out-of-stock'],
|
||
|
|
simpleDescription: 'Premium Windows laptop',
|
||
|
|
description: [
|
||
|
|
{ key: 'Processor', value: 'Intel Core i9' },
|
||
|
|
{ key: 'RAM', value: '32GB' }
|
||
|
|
],
|
||
|
|
subcategoryId: 'sub2'
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
// Generate more items for testing infinite scroll
|
||
|
|
private generateMoreItems(subcategoryId: string, count: number): Item[] {
|
||
|
|
const items: Item[] = [];
|
||
|
|
for (let i = 6; i <= count + 5; i++) {
|
||
|
|
items.push({
|
||
|
|
id: `item${i}`,
|
||
|
|
name: `Test Product ${i}`,
|
||
|
|
visible: Math.random() > 0.3,
|
||
|
|
priority: i,
|
||
|
|
quantity: Math.floor(Math.random() * 100),
|
||
|
|
price: Math.floor(Math.random() * 1000) + 100,
|
||
|
|
currency: 'USD',
|
||
|
|
imgs: [`https://via.placeholder.com/600x400?text=Product+${i}`],
|
||
|
|
tags: ['test'],
|
||
|
|
simpleDescription: `This is test product number ${i}`,
|
||
|
|
description: [{ key: 'Size', value: 'Medium' }],
|
||
|
|
subcategoryId
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return items;
|
||
|
|
}
|
||
|
|
|
||
|
|
getProjects(): Observable<Project[]> {
|
||
|
|
return of(this.projects).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
getCategories(projectId: string): Observable<Category[]> {
|
||
|
|
const cats = this.categories.filter(c => c.projectId === projectId);
|
||
|
|
return of(cats).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
getCategory(categoryId: string): Observable<Category> {
|
||
|
|
const cat = this.categories.find(c => c.id === categoryId)!;
|
||
|
|
return of(cat).pipe(delay(200));
|
||
|
|
}
|
||
|
|
|
||
|
|
updateCategory(categoryId: string, data: Partial<Category>): Observable<Category> {
|
||
|
|
const cat = this.categories.find(c => c.id === categoryId)!;
|
||
|
|
Object.assign(cat, data);
|
||
|
|
return of(cat).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
createCategory(projectId: string, data: Partial<Category>): Observable<Category> {
|
||
|
|
const newCat: Category = {
|
||
|
|
id: `cat${Date.now()}`,
|
||
|
|
name: data.name || 'New Category',
|
||
|
|
visible: data.visible ?? true,
|
||
|
|
priority: data.priority || 99,
|
||
|
|
img: data.img,
|
||
|
|
projectId,
|
||
|
|
subcategories: []
|
||
|
|
};
|
||
|
|
this.categories.push(newCat);
|
||
|
|
return of(newCat).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
deleteCategory(categoryId: string): Observable<void> {
|
||
|
|
const index = this.categories.findIndex(c => c.id === categoryId);
|
||
|
|
if (index > -1) this.categories.splice(index, 1);
|
||
|
|
return of(void 0).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
getSubcategory(subcategoryId: string): Observable<Subcategory> {
|
||
|
|
let sub: Subcategory | undefined;
|
||
|
|
for (const cat of this.categories) {
|
||
|
|
sub = cat.subcategories?.find(s => s.id === subcategoryId);
|
||
|
|
if (sub) break;
|
||
|
|
}
|
||
|
|
return of(sub!).pipe(delay(200));
|
||
|
|
}
|
||
|
|
|
||
|
|
updateSubcategory(subcategoryId: string, data: Partial<Subcategory>): Observable<Subcategory> {
|
||
|
|
let sub: Subcategory | undefined;
|
||
|
|
for (const cat of this.categories) {
|
||
|
|
sub = cat.subcategories?.find(s => s.id === subcategoryId);
|
||
|
|
if (sub) {
|
||
|
|
Object.assign(sub, data);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return of(sub!).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
createSubcategory(categoryId: string, data: Partial<Subcategory>): Observable<Subcategory> {
|
||
|
|
const cat = this.categories.find(c => c.id === categoryId)!;
|
||
|
|
const newSub: Subcategory = {
|
||
|
|
id: `sub${Date.now()}`,
|
||
|
|
name: data.name || 'New Subcategory',
|
||
|
|
visible: data.visible ?? true,
|
||
|
|
priority: data.priority || 99,
|
||
|
|
img: data.img,
|
||
|
|
categoryId,
|
||
|
|
itemCount: 0
|
||
|
|
};
|
||
|
|
if (!cat.subcategories) cat.subcategories = [];
|
||
|
|
cat.subcategories.push(newSub);
|
||
|
|
return of(newSub).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
deleteSubcategory(subcategoryId: string): Observable<void> {
|
||
|
|
for (const cat of this.categories) {
|
||
|
|
const index = cat.subcategories?.findIndex(s => s.id === subcategoryId) ?? -1;
|
||
|
|
if (index > -1) {
|
||
|
|
cat.subcategories?.splice(index, 1);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return of(void 0).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
getItems(subcategoryId: string, page = 1, limit = 20, search?: string, filters?: any): Observable<ItemsListResponse> {
|
||
|
|
let allItems = [...this.items, ...this.generateMoreItems(subcategoryId, 50)];
|
||
|
|
|
||
|
|
// Filter by subcategory
|
||
|
|
allItems = allItems.filter(item => item.subcategoryId === subcategoryId);
|
||
|
|
|
||
|
|
// Apply search
|
||
|
|
if (search) {
|
||
|
|
allItems = allItems.filter(item =>
|
||
|
|
item.name.toLowerCase().includes(search.toLowerCase())
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Apply visibility filter
|
||
|
|
if (filters?.visible !== undefined) {
|
||
|
|
allItems = allItems.filter(item => item.visible === filters.visible);
|
||
|
|
}
|
||
|
|
|
||
|
|
const total = allItems.length;
|
||
|
|
const start = (page - 1) * limit;
|
||
|
|
const end = start + limit;
|
||
|
|
const items = allItems.slice(start, end);
|
||
|
|
|
||
|
|
return of({
|
||
|
|
items,
|
||
|
|
total,
|
||
|
|
page,
|
||
|
|
limit,
|
||
|
|
hasMore: end < total
|
||
|
|
}).pipe(delay(400));
|
||
|
|
}
|
||
|
|
|
||
|
|
getItem(itemId: string): Observable<Item> {
|
||
|
|
const item = this.items.find(i => i.id === itemId)!;
|
||
|
|
return of(item).pipe(delay(200));
|
||
|
|
}
|
||
|
|
|
||
|
|
updateItem(itemId: string, data: Partial<Item>): Observable<Item> {
|
||
|
|
const item = this.items.find(i => i.id === itemId)!;
|
||
|
|
Object.assign(item, data);
|
||
|
|
return of(item).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
createItem(subcategoryId: string, data: Partial<Item>): Observable<Item> {
|
||
|
|
const newItem: Item = {
|
||
|
|
id: `item${Date.now()}`,
|
||
|
|
name: data.name || 'New Item',
|
||
|
|
visible: data.visible ?? true,
|
||
|
|
priority: data.priority || 99,
|
||
|
|
quantity: data.quantity || 0,
|
||
|
|
price: data.price || 0,
|
||
|
|
currency: data.currency || 'USD',
|
||
|
|
imgs: data.imgs || [],
|
||
|
|
tags: data.tags || [],
|
||
|
|
simpleDescription: data.simpleDescription || '',
|
||
|
|
description: data.description || [],
|
||
|
|
subcategoryId
|
||
|
|
};
|
||
|
|
this.items.push(newItem);
|
||
|
|
return of(newItem).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
deleteItem(itemId: string): Observable<void> {
|
||
|
|
const index = this.items.findIndex(i => i.id === itemId);
|
||
|
|
if (index > -1) this.items.splice(index, 1);
|
||
|
|
return of(void 0).pipe(delay(300));
|
||
|
|
}
|
||
|
|
|
||
|
|
bulkUpdateItems(itemIds: string[], data: Partial<Item>): Observable<void> {
|
||
|
|
itemIds.forEach(id => {
|
||
|
|
const item = this.items.find(i => i.id === id);
|
||
|
|
if (item) Object.assign(item, data);
|
||
|
|
});
|
||
|
|
return of(void 0).pipe(delay(400));
|
||
|
|
}
|
||
|
|
|
||
|
|
uploadImage(file: File): Observable<{ url: string }> {
|
||
|
|
// Simulate upload
|
||
|
|
const url = `https://via.placeholder.com/600x400?text=${encodeURIComponent(file.name)}`;
|
||
|
|
return of({ url }).pipe(delay(1000));
|
||
|
|
}
|
||
|
|
}
|