✅ product admin crud
This commit is contained in:
@@ -1,4 +1,14 @@
|
||||
import { Database } from "@pkg/db";
|
||||
import { desc, eq, type Database } from "@pkg/db";
|
||||
import { product } from "@pkg/db/schema";
|
||||
import { getError, Logger } from "@pkg/logger";
|
||||
import { ERROR_CODES, type Result } from "@pkg/result";
|
||||
import { nanoid } from "nanoid";
|
||||
import {
|
||||
productModel,
|
||||
type CreateProductPayload,
|
||||
type ProductModel,
|
||||
type UpdateProductPayload,
|
||||
} from "./data";
|
||||
|
||||
export class ProductRepository {
|
||||
private db: Database;
|
||||
@@ -7,9 +17,277 @@ export class ProductRepository {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
// TODO: compelte the crud method implementation
|
||||
async listAllProducts() {}
|
||||
async createProduct() {}
|
||||
async updateProduct() {}
|
||||
async deleteProduct() {}
|
||||
async listAllProducts(): Promise<Result<ProductModel[]>> {
|
||||
try {
|
||||
const results = await this.db.query.product.findMany({
|
||||
orderBy: [desc(product.createdAt)],
|
||||
});
|
||||
const out = [] as ProductModel[];
|
||||
for (const result of results) {
|
||||
const parsed = productModel.safeParse(result);
|
||||
if (!parsed.success) {
|
||||
Logger.error("Failed to parse product");
|
||||
Logger.error(parsed.error);
|
||||
continue;
|
||||
}
|
||||
out.push(parsed.data);
|
||||
}
|
||||
return { data: out };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to fetch products",
|
||||
detail:
|
||||
"An error occurred while retrieving products from the database",
|
||||
userHint: "Please try refreshing the page",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async getProductById(id: number): Promise<Result<ProductModel>> {
|
||||
try {
|
||||
const result = await this.db.query.product.findFirst({
|
||||
where: eq(product.id, id),
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.NOT_FOUND_ERROR,
|
||||
message: "Product not found",
|
||||
detail: "No product exists with the provided ID",
|
||||
userHint: "Please check the product ID and try again",
|
||||
actionable: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const parsed = productModel.safeParse(result);
|
||||
if (!parsed.success) {
|
||||
Logger.error("Failed to parse product", result);
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
||||
message: "Failed to parse product",
|
||||
userHint: "Please try again",
|
||||
detail: "Failed to parse product",
|
||||
}),
|
||||
};
|
||||
}
|
||||
return { data: parsed.data };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to fetch product",
|
||||
detail:
|
||||
"An error occurred while retrieving the product from the database",
|
||||
userHint: "Please try refreshing the page",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async createProduct(payload: CreateProductPayload): Promise<Result<number>> {
|
||||
try {
|
||||
// Generate a unique linkId
|
||||
const linkId = nanoid(16);
|
||||
|
||||
const result = await this.db
|
||||
.insert(product)
|
||||
.values({
|
||||
linkId,
|
||||
title: payload.title,
|
||||
description: payload.description,
|
||||
longDescription: payload.longDescription,
|
||||
price: payload.price.toString(),
|
||||
discountPrice: payload.discountPrice.toString(),
|
||||
})
|
||||
.returning({ id: product.id })
|
||||
.execute();
|
||||
|
||||
if (!result || result.length === 0) {
|
||||
throw new Error("Failed to create product record");
|
||||
}
|
||||
|
||||
return { data: result[0].id };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to create product",
|
||||
detail: "An error occurred while creating the product",
|
||||
userHint: "Please try again",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async updateProduct(payload: UpdateProductPayload): Promise<Result<boolean>> {
|
||||
try {
|
||||
if (!payload.id) {
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.VALIDATION_ERROR,
|
||||
message: "Invalid product ID",
|
||||
detail: "No product ID was provided for the update operation",
|
||||
userHint: "Please provide a valid product ID",
|
||||
actionable: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// Check if product exists
|
||||
const existing = await this.db.query.product.findFirst({
|
||||
where: eq(product.id, payload.id),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.NOT_FOUND_ERROR,
|
||||
message: "Product not found",
|
||||
detail: "No product exists with the provided ID",
|
||||
userHint: "Please check the product ID and try again",
|
||||
actionable: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// Build the update object with only the fields that are provided
|
||||
const updateValues: Record<string, any> = {};
|
||||
|
||||
if (payload.title !== undefined) updateValues.title = payload.title;
|
||||
if (payload.description !== undefined)
|
||||
updateValues.description = payload.description;
|
||||
if (payload.longDescription !== undefined)
|
||||
updateValues.longDescription = payload.longDescription;
|
||||
if (payload.price !== undefined)
|
||||
updateValues.price = payload.price.toString();
|
||||
if (payload.discountPrice !== undefined)
|
||||
updateValues.discountPrice = payload.discountPrice.toString();
|
||||
updateValues.updatedAt = new Date();
|
||||
|
||||
await this.db
|
||||
.update(product)
|
||||
.set(updateValues)
|
||||
.where(eq(product.id, payload.id))
|
||||
.execute();
|
||||
|
||||
return { data: true };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to update product",
|
||||
detail: "An error occurred while updating the product",
|
||||
userHint: "Please try again",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async deleteProduct(id: number): Promise<Result<boolean>> {
|
||||
try {
|
||||
// Check if product exists
|
||||
const existing = await this.db.query.product.findFirst({
|
||||
where: eq(product.id, id),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.NOT_FOUND_ERROR,
|
||||
message: "Product not found",
|
||||
detail: "No product exists with the provided ID",
|
||||
userHint: "Please check the product ID and try again",
|
||||
actionable: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
await this.db.delete(product).where(eq(product.id, id)).execute();
|
||||
|
||||
return { data: true };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to delete product",
|
||||
detail: "An error occurred while deleting the product",
|
||||
userHint: "Please try again",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async refreshProductLinkId(id: number): Promise<Result<string>> {
|
||||
try {
|
||||
// Check if product exists
|
||||
const existing = await this.db.query.product.findFirst({
|
||||
where: eq(product.id, id),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return {
|
||||
error: getError({
|
||||
code: ERROR_CODES.NOT_FOUND_ERROR,
|
||||
message: "Product not found",
|
||||
detail: "No product exists with the provided ID",
|
||||
userHint: "Please check the product ID and try again",
|
||||
actionable: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// Generate a new unique linkId
|
||||
const newLinkId = nanoid(16);
|
||||
|
||||
await this.db
|
||||
.update(product)
|
||||
.set({
|
||||
linkId: newLinkId,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(product.id, id))
|
||||
.execute();
|
||||
|
||||
return { data: newLinkId };
|
||||
} catch (e) {
|
||||
return {
|
||||
error: getError(
|
||||
{
|
||||
code: ERROR_CODES.DATABASE_ERROR,
|
||||
message: "Failed to refresh product link ID",
|
||||
detail: "An error occurred while refreshing the product's link ID",
|
||||
userHint: "Please try again",
|
||||
actionable: false,
|
||||
},
|
||||
e,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user