This commit is contained in:
phaichayon
2026-06-11 12:46:57 +07:00
parent 1993d88306
commit 7a390cf0df
720 changed files with 100919 additions and 0 deletions

View 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
View 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();