add product image and variant value
This commit is contained in:
@@ -39,3 +39,20 @@
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## 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
@@ -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 {
|
||||
variantId: string;
|
||||
productTitle?: string;
|
||||
variantValue?: string;
|
||||
image?: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
discount: number;
|
||||
|
||||
@@ -21,10 +21,13 @@ export class CartItemService {
|
||||
createCartItem(variant: Variant, quantity: number): CartItem {
|
||||
const itemPrice = variant.price || 0;
|
||||
const itemDiscount = variant.product.discount || 0;
|
||||
const image = variant.product.images?.[0];
|
||||
|
||||
return {
|
||||
variantId: variant.id,
|
||||
productTitle: variant.product.title,
|
||||
variantValue: variant.value,
|
||||
image,
|
||||
quantity,
|
||||
price: itemPrice,
|
||||
discount: itemDiscount,
|
||||
@@ -38,10 +41,13 @@ export class CartItemService {
|
||||
buildCartItemFromFood(variant: Variant, quantity: number, previous?: CartItem): CartItem {
|
||||
const itemPrice = variant.price || 0;
|
||||
const itemDiscount = variant.product.discount || 0;
|
||||
const image = variant.product.images?.[0];
|
||||
return {
|
||||
...(previous ?? ({} as CartItem)),
|
||||
variantId: variant.id,
|
||||
productTitle: variant.product.title,
|
||||
variantValue: variant.value,
|
||||
image,
|
||||
quantity,
|
||||
price: itemPrice,
|
||||
discount: itemDiscount,
|
||||
|
||||
@@ -28,6 +28,7 @@ export class FoodController {
|
||||
@Get('public/products/shop/:slug')
|
||||
@ApiOperation({ summary: 'Get all products by shop slug' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiQuery({ name: 'categoryId', required: false, type: String })
|
||||
findAllByRestaurant(@Param('slug') slug: string, @Query('categoryId') categoryId?: string) {
|
||||
return this.productService.findByShop(slug, categoryId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user