init
This commit is contained in:
191
src/constants/mock-api-users.ts
Normal file
191
src/constants/mock-api-users.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 🛑 Nothing in here has anything to do with Nextjs, it's just a fake database
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { matchSorter } from 'match-sorter';
|
||||
|
||||
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export type User = {
|
||||
id: number;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
status: string;
|
||||
role: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
// Mock user data store
|
||||
export const fakeUsers = {
|
||||
records: [] as User[],
|
||||
|
||||
initialize() {
|
||||
const sampleUsers: User[] = [];
|
||||
function generateRandomUserData(id: number): User {
|
||||
const roles = ['Developer', 'Designer', 'Manager', 'QA', 'DevOps', 'Product Owner'];
|
||||
const statuses = ['Active', 'Inactive', 'Invited'];
|
||||
|
||||
return {
|
||||
id,
|
||||
first_name: faker.person.firstName(),
|
||||
last_name: faker.person.lastName(),
|
||||
email: faker.internet.email(),
|
||||
phone: faker.phone.number({ style: 'national' }),
|
||||
status: faker.helpers.arrayElement(statuses),
|
||||
role: faker.helpers.arrayElement(roles),
|
||||
created_at: faker.date.between({ from: '2022-01-01', to: '2023-12-31' }).toISOString(),
|
||||
updated_at: faker.date.recent().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 50; i++) {
|
||||
sampleUsers.push(generateRandomUserData(i));
|
||||
}
|
||||
|
||||
this.records = sampleUsers;
|
||||
},
|
||||
|
||||
async getAll({ roles = [], search }: { roles?: string[]; search?: string }) {
|
||||
let users = [...this.records];
|
||||
|
||||
if (roles.length > 0) {
|
||||
users = users.filter((user) => roles.includes(user.role));
|
||||
}
|
||||
|
||||
if (search) {
|
||||
users = matchSorter(users, search, {
|
||||
keys: ['first_name', 'last_name', 'email']
|
||||
});
|
||||
}
|
||||
|
||||
return users;
|
||||
},
|
||||
|
||||
async createUser(data: Omit<User, 'id' | 'created_at' | 'updated_at'>) {
|
||||
await delay(800);
|
||||
|
||||
const newUser: User = {
|
||||
...data,
|
||||
id: this.records.length + 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.records.push(newUser);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'User created successfully',
|
||||
user: newUser
|
||||
};
|
||||
},
|
||||
|
||||
async updateUser(id: number, data: Omit<User, 'id' | 'created_at' | 'updated_at'>) {
|
||||
await delay(800);
|
||||
|
||||
const index = this.records.findIndex((user) => user.id === id);
|
||||
|
||||
if (index === -1) {
|
||||
return { success: false, message: `User with ID ${id} not found` };
|
||||
}
|
||||
|
||||
this.records[index] = {
|
||||
...this.records[index],
|
||||
...data,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'User updated successfully',
|
||||
user: this.records[index]
|
||||
};
|
||||
},
|
||||
|
||||
async deleteUser(id: number) {
|
||||
await delay(800);
|
||||
|
||||
const index = this.records.findIndex((user) => user.id === id);
|
||||
|
||||
if (index === -1) {
|
||||
return { success: false, message: `User with ID ${id} not found` };
|
||||
}
|
||||
|
||||
this.records.splice(index, 1);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'User deleted successfully'
|
||||
};
|
||||
},
|
||||
|
||||
async getUsers({
|
||||
page = 1,
|
||||
limit = 10,
|
||||
roles,
|
||||
search,
|
||||
sort
|
||||
}: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
roles?: string | string[];
|
||||
search?: string;
|
||||
sort?: string;
|
||||
}) {
|
||||
await delay(800);
|
||||
const rolesArray = roles ? (Array.isArray(roles) ? roles : String(roles).split(/[.,]/)) : [];
|
||||
const allUsers = await this.getAll({
|
||||
roles: rolesArray,
|
||||
search
|
||||
});
|
||||
|
||||
// Sorting
|
||||
if (sort) {
|
||||
try {
|
||||
const sortItems = JSON.parse(sort) as {
|
||||
id: string;
|
||||
desc: boolean;
|
||||
}[];
|
||||
if (sortItems.length > 0) {
|
||||
const { id, desc } = sortItems[0];
|
||||
allUsers.sort((a, b) => {
|
||||
// Handle computed 'name' column
|
||||
const aVal =
|
||||
id === 'name' ? `${a.first_name} ${a.last_name}` : (a as Record<string, unknown>)[id];
|
||||
const bVal =
|
||||
id === 'name' ? `${b.first_name} ${b.last_name}` : (b as Record<string, unknown>)[id];
|
||||
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
||||
return desc ? bVal - aVal : aVal - bVal;
|
||||
}
|
||||
const aStr = String(aVal ?? '').toLowerCase();
|
||||
const bStr = String(bVal ?? '').toLowerCase();
|
||||
return desc ? bStr.localeCompare(aStr) : aStr.localeCompare(bStr);
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Invalid sort param — ignore
|
||||
}
|
||||
}
|
||||
|
||||
const totalUsers = allUsers.length;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
const paginatedUsers = allUsers.slice(offset, offset + limit);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
time: new Date().toISOString(),
|
||||
message: 'Sample data for testing and learning purposes',
|
||||
total_users: totalUsers,
|
||||
offset,
|
||||
limit,
|
||||
users: paginatedUsers
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fakeUsers.initialize();
|
||||
246
src/constants/mock-api.ts
Normal file
246
src/constants/mock-api.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 🛑 Nothing in here has anything to do with Nextjs, it's just a fake database
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { matchSorter } from 'match-sorter'; // For filtering
|
||||
|
||||
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
// Define the shape of Product data
|
||||
export type Product = {
|
||||
photo_url: string;
|
||||
name: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
price: number;
|
||||
id: number;
|
||||
category: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
// Mock product data store
|
||||
export const fakeProducts = {
|
||||
records: [] as Product[], // Holds the list of product objects
|
||||
|
||||
// Initialize with sample data
|
||||
initialize() {
|
||||
const sampleProducts: Product[] = [];
|
||||
function generateRandomProductData(id: number): Product {
|
||||
const categories = [
|
||||
'Electronics',
|
||||
'Furniture',
|
||||
'Clothing',
|
||||
'Toys',
|
||||
'Groceries',
|
||||
'Books',
|
||||
'Jewelry',
|
||||
'Beauty Products'
|
||||
];
|
||||
|
||||
return {
|
||||
id,
|
||||
name: faker.commerce.productName(),
|
||||
description: faker.commerce.productDescription(),
|
||||
created_at: faker.date.between({ from: '2022-01-01', to: '2023-12-31' }).toISOString(),
|
||||
price: parseFloat(faker.commerce.price({ min: 5, max: 500, dec: 2 })),
|
||||
photo_url: `https://api.slingacademy.com/public/sample-products/${id}.png`,
|
||||
category: faker.helpers.arrayElement(categories),
|
||||
updated_at: faker.date.recent().toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// Generate remaining records
|
||||
for (let i = 1; i <= 20; i++) {
|
||||
sampleProducts.push(generateRandomProductData(i));
|
||||
}
|
||||
|
||||
this.records = sampleProducts;
|
||||
},
|
||||
|
||||
// Get all products with optional category filtering and search
|
||||
async getAll({ categories = [], search }: { categories?: string[]; search?: string }) {
|
||||
let products = [...this.records];
|
||||
|
||||
// Filter products based on selected categories
|
||||
if (categories.length > 0) {
|
||||
products = products.filter((product) => categories.includes(product.category));
|
||||
}
|
||||
|
||||
// Search functionality across multiple fields
|
||||
if (search) {
|
||||
products = matchSorter(products, search, {
|
||||
keys: ['name', 'description', 'category']
|
||||
});
|
||||
}
|
||||
|
||||
return products;
|
||||
},
|
||||
|
||||
// Get paginated results with optional category filtering, search, and sorting
|
||||
async getProducts({
|
||||
page = 1,
|
||||
limit = 10,
|
||||
categories,
|
||||
search,
|
||||
sort
|
||||
}: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
categories?: string | string[];
|
||||
search?: string;
|
||||
sort?: string;
|
||||
}) {
|
||||
await delay(1000);
|
||||
const categoriesArray = categories
|
||||
? Array.isArray(categories)
|
||||
? categories
|
||||
: String(categories).split(/[.,]/)
|
||||
: [];
|
||||
const allProducts = await this.getAll({
|
||||
categories: categoriesArray,
|
||||
search
|
||||
});
|
||||
|
||||
// Sorting
|
||||
if (sort) {
|
||||
try {
|
||||
const sortItems = JSON.parse(sort) as {
|
||||
id: string;
|
||||
desc: boolean;
|
||||
}[];
|
||||
if (sortItems.length > 0) {
|
||||
const { id, desc } = sortItems[0];
|
||||
allProducts.sort((a, b) => {
|
||||
const aVal = (a as Record<string, unknown>)[id];
|
||||
const bVal = (b as Record<string, unknown>)[id];
|
||||
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
||||
return desc ? bVal - aVal : aVal - bVal;
|
||||
}
|
||||
const aStr = String(aVal ?? '').toLowerCase();
|
||||
const bStr = String(bVal ?? '').toLowerCase();
|
||||
return desc ? bStr.localeCompare(aStr) : aStr.localeCompare(bStr);
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Invalid sort param — ignore
|
||||
}
|
||||
}
|
||||
|
||||
const totalProducts = allProducts.length;
|
||||
|
||||
// Pagination logic
|
||||
const offset = (page - 1) * limit;
|
||||
const paginatedProducts = allProducts.slice(offset, offset + limit);
|
||||
|
||||
// Mock current time
|
||||
const currentTime = new Date().toISOString();
|
||||
|
||||
// Return paginated response
|
||||
return {
|
||||
success: true,
|
||||
time: currentTime,
|
||||
message: 'Sample data for testing and learning purposes',
|
||||
total_products: totalProducts,
|
||||
offset,
|
||||
limit,
|
||||
products: paginatedProducts
|
||||
};
|
||||
},
|
||||
|
||||
// Get a specific product by its ID
|
||||
async getProductById(id: number) {
|
||||
await delay(3000); // Simulate a slow API call
|
||||
|
||||
// Find the product by its ID
|
||||
const product = this.records.find((product) => product.id === id);
|
||||
|
||||
if (!product) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Product with ID ${id} not found`
|
||||
};
|
||||
}
|
||||
|
||||
// Mock current time
|
||||
const currentTime = new Date().toISOString();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
time: currentTime,
|
||||
message: `Product with ID ${id} found`,
|
||||
product
|
||||
};
|
||||
},
|
||||
|
||||
// Create a new product
|
||||
async createProduct(data: Omit<Product, 'id' | 'created_at' | 'updated_at' | 'photo_url'>) {
|
||||
await delay(1000);
|
||||
|
||||
const newProduct: Product = {
|
||||
...data,
|
||||
id: this.records.length + 1,
|
||||
photo_url: `https://api.slingacademy.com/public/sample-products/${this.records.length + 1}.png`,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.records.push(newProduct);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Product created successfully',
|
||||
product: newProduct
|
||||
};
|
||||
},
|
||||
|
||||
// Update an existing product
|
||||
async updateProduct(
|
||||
id: number,
|
||||
data: Omit<Product, 'id' | 'created_at' | 'updated_at' | 'photo_url'>
|
||||
) {
|
||||
await delay(1000);
|
||||
|
||||
const index = this.records.findIndex((product) => product.id === id);
|
||||
|
||||
if (index === -1) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Product with ID ${id} not found`
|
||||
};
|
||||
}
|
||||
|
||||
this.records[index] = {
|
||||
...this.records[index],
|
||||
...data,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Product updated successfully',
|
||||
product: this.records[index]
|
||||
};
|
||||
},
|
||||
|
||||
// Delete a product
|
||||
async deleteProduct(id: number) {
|
||||
await delay(1000);
|
||||
|
||||
const index = this.records.findIndex((product) => product.id === id);
|
||||
|
||||
if (index === -1) {
|
||||
return { success: false, message: `Product with ID ${id} not found` };
|
||||
}
|
||||
|
||||
this.records.splice(index, 1);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Product deleted successfully'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize sample products
|
||||
fakeProducts.initialize();
|
||||
Reference in New Issue
Block a user