add product image and variant value

This commit is contained in:
2026-02-15 10:22:07 +03:30
parent 2b3abfc599
commit 98cf3d0a92
8 changed files with 129 additions and 91946 deletions
+17
View File
@@ -39,3 +39,20 @@
- Review all names before running the populate script - Review all names before running the populate script
The final CSV will have all original columns plus the new "icon_name" column with your custom names. The final CSV will have all original columns plus the new "icon_name" column with your custom names.
---
## Import named icons into the database
To insert icons from `named_icons.csv` into the `icons` table:
1. **Ensure your database is running** and `.env` in the project root has `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, and `DB_PASS` (same as for `dump-icons.sh` / `restore-icons.sh`).
2. **Optional:** Set which icon group to attach icons to:
- `ICON_GROUP_ID` (default: `01KD7DG4VCTWZRSA54M3PEJ6NW` — پک آیکون 1). Use an existing `icon_groups.id` from your DB.
3. **From the project root**, run:
```bash
node dump/import-named-icons.js
```
The script reads `dump/named_icons.csv` (columns: Index, Name, URL) and inserts one row per URL into `icons` with generated ULID and timestamps. The **Name** column is not stored (the `icons` table has no name column); only **URL** and **group_id** are used.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env node
/**
* Import icons from dump/named_icons.csv into the icons table.
* Uses DB_* env vars (from .env or environment). Icons are assigned to ICON_GROUP_ID.
*
* Run from project root: node dump/import-named-icons.js
*/
const fs = require('fs');
const path = require('path');
const { Client } = require('pg');
const { ulid } = require('ulid');
// Load .env from project root
const envPath = path.join(__dirname, '..', '.env');
if (fs.existsSync(envPath)) {
const content = fs.readFileSync(envPath, 'utf8');
content.split('\n').forEach((line) => {
const trimmed = line.replace(/^\s+|\s+$/, '').replace(/#.*$/, '').trim();
if (!trimmed || !/^[A-Za-z_][A-Za-z0-9_]*=/.test(trimmed)) return;
const eq = trimmed.indexOf('=');
const key = trimmed.slice(0, eq);
const value = trimmed.slice(eq + 1).replace(/^["']|["']$/g, '');
if (!process.env[key]) process.env[key] = value;
});
}
const DB_HOST = process.env.DB_HOST || 'localhost';
const DB_PORT = process.env.DB_PORT || '5432';
const DB_NAME = process.env.DB_NAME || 'dmenu';
const DB_USER = process.env.DB_USER || 'postgres';
const DB_PASS = process.env.DB_PASS || '';
const ICON_GROUP_ID = process.env.ICON_GROUP_ID || '01KD7DG4VCTWZRSA54M3PEJ6NW';
const csvPath = path.join(__dirname, 'named_icons.csv');
function parseCsvLine(line) {
const parts = line.split(',');
if (parts.length < 3) return null;
const index = parts[0].trim();
const url = parts[parts.length - 1].trim();
const name = parts.slice(1, -1).join(',').trim();
return { index, name, url };
}
async function main() {
if (!fs.existsSync(csvPath)) {
console.error('Error: named_icons.csv not found at', csvPath);
process.exit(1);
}
const csvContent = fs.readFileSync(csvPath, 'utf8');
const lines = csvContent.split('\n').filter((l) => l.trim());
const header = lines[0];
const dataLines = lines.slice(1);
const rows = [];
for (const line of dataLines) {
const row = parseCsvLine(line);
if (row && row.url) rows.push(row);
}
if (rows.length === 0) {
console.error('Error: No valid rows found in named_icons.csv');
process.exit(1);
}
const client = new Client({
host: DB_HOST,
port: parseInt(DB_PORT, 10),
database: DB_NAME,
user: DB_USER,
password: DB_PASS,
});
try {
await client.connect();
} catch (err) {
console.error('Database connection failed:', err.message);
process.exit(1);
}
const now = new Date().toISOString();
let inserted = 0;
for (const row of rows) {
const id = ulid();
await client.query(
`INSERT INTO icons (id, created_at, updated_at, deleted_at, url, group_id)
VALUES ($1, $2::timestamptz, $3::timestamptz, NULL, $4, $5)`,
[id, now, now, row.url, ICON_GROUP_ID]
);
inserted++;
}
await client.end();
console.log(`Done. Inserted ${inserted} icons.`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -3,6 +3,8 @@ import type { OrderCouponDetail, OrderUserAddress } from 'src/modules/orders/int
export interface CartItem { export interface CartItem {
variantId: string; variantId: string;
productTitle?: string; productTitle?: string;
variantValue?: string;
image?: string;
quantity: number; quantity: number;
price: number; price: number;
discount: number; discount: number;
@@ -21,10 +21,13 @@ export class CartItemService {
createCartItem(variant: Variant, quantity: number): CartItem { createCartItem(variant: Variant, quantity: number): CartItem {
const itemPrice = variant.price || 0; const itemPrice = variant.price || 0;
const itemDiscount = variant.product.discount || 0; const itemDiscount = variant.product.discount || 0;
const image = variant.product.images?.[0];
return { return {
variantId: variant.id, variantId: variant.id,
productTitle: variant.product.title, productTitle: variant.product.title,
variantValue: variant.value,
image,
quantity, quantity,
price: itemPrice, price: itemPrice,
discount: itemDiscount, discount: itemDiscount,
@@ -38,10 +41,13 @@ export class CartItemService {
buildCartItemFromFood(variant: Variant, quantity: number, previous?: CartItem): CartItem { buildCartItemFromFood(variant: Variant, quantity: number, previous?: CartItem): CartItem {
const itemPrice = variant.price || 0; const itemPrice = variant.price || 0;
const itemDiscount = variant.product.discount || 0; const itemDiscount = variant.product.discount || 0;
const image = variant.product.images?.[0];
return { return {
...(previous ?? ({} as CartItem)), ...(previous ?? ({} as CartItem)),
variantId: variant.id, variantId: variant.id,
productTitle: variant.product.title, productTitle: variant.product.title,
variantValue: variant.value,
image,
quantity, quantity,
price: itemPrice, price: itemPrice,
discount: itemDiscount, discount: itemDiscount,
@@ -28,6 +28,7 @@ export class FoodController {
@Get('public/products/shop/:slug') @Get('public/products/shop/:slug')
@ApiOperation({ summary: 'Get all products by shop slug' }) @ApiOperation({ summary: 'Get all products by shop slug' })
@ApiHeader(API_HEADER_SLUG) @ApiHeader(API_HEADER_SLUG)
@ApiQuery({ name: 'categoryId', required: false, type: String })
findAllByRestaurant(@Param('slug') slug: string, @Query('categoryId') categoryId?: string) { findAllByRestaurant(@Param('slug') slug: string, @Query('categoryId') categoryId?: string) {
return this.productService.findByShop(slug, categoryId); return this.productService.findByShop(slug, categoryId);
} }