diff --git a/src/modules/market/components/CreateProductDialog.vue b/src/modules/market/components/CreateProductDialog.vue index 636cff7..93d6042 100644 --- a/src/modules/market/components/CreateProductDialog.vue +++ b/src/modules/market/components/CreateProductDialog.vue @@ -219,7 +219,8 @@ import { FormMessage, } from '@/components/ui/form' import { Package } from 'lucide-vue-next' -import type { NostrmarketAPI, Stall, Product, CreateProductRequest } from '../services/nostrmarketAPI' +import type { NostrmarketAPI, Stall, CreateProductRequest } from '../services/nostrmarketAPI' +import type { Product } from '../types/market' import { auth } from '@/composables/useAuthService' import { useToast } from '@/core/composables/useToast' import { injectService, SERVICE_TOKENS } from '@/core/di-container' @@ -326,9 +327,9 @@ const updateProduct = async (formData: any) => { createError.value = null try { - const productData: Product = { - id: props.product.id, - stall_id: props.product.stall_id, + const productData = { + id: props.product?.id, + stall_id: props.product?.stall_id || props.stall?.id || '', name, categories: categories || [], images: images || [], @@ -338,11 +339,13 @@ const updateProduct = async (formData: any) => { pending: false, config: { description: description || '', - currency: props.stall?.currency || props.product.config.currency, + currency: props.stall?.currency || props.product?.config?.currency || 'sats', use_autoreply, autoreply_message: use_autoreply ? autoreply_message || '' : '', - shipping: props.product.config.shipping || [] - } + shipping: props.product?.config?.shipping || [] + }, + event_id: props.product?.nostrEventId, + event_created_at: props.product?.createdAt } const adminKey = paymentService.getPreferredWalletAdminKey() diff --git a/src/modules/market/components/MerchantStore.vue b/src/modules/market/components/MerchantStore.vue index f4ea030..6e4d84d 100644 --- a/src/modules/market/components/MerchantStore.vue +++ b/src/modules/market/components/MerchantStore.vue @@ -258,7 +258,7 @@

{{ product.name }}

-

+

{{ product.config.description }}

@@ -266,7 +266,7 @@
- {{ product.price }} {{ product.config.currency || activeStall?.currency || 'sat' }} + {{ product.price }} {{ product.config?.currency || activeStall?.currency || 'sat' }}
Qty: {{ product.quantity }} @@ -342,7 +342,9 @@ import { AlertCircle, User } from 'lucide-vue-next' -import type { NostrmarketAPI, Merchant, Stall, Product } from '../services/nostrmarketAPI' +import type { NostrmarketAPI, Merchant, Stall } from '../services/nostrmarketAPI' +import type { Product } from '../types/market' +import { mapApiResponseToProduct } from '../types/market' import { auth } from '@/composables/useAuthService' import { useToast } from '@/core/composables/useToast' import { injectService, SERVICE_TOKENS } from '@/core/di-container' @@ -516,26 +518,14 @@ const loadStallProducts = async () => { inkey, activeStall.value.id! ) - // Enrich products with stall name and missing properties to match Product interface - const enrichedProducts = (products || []).map(product => ({ - id: product.id || `${product.stall_id}-${Date.now()}`, // Ensure id is always string - stall_id: product.stall_id, - stallName: activeStall.value?.name || 'Unknown Stall', - name: product.name, - description: product.config?.description || '', - price: product.price, - currency: activeStall.value?.currency || 'sats', // Use stall currency - quantity: product.quantity, - images: product.images, - categories: product.categories, - createdAt: product.event_created_at || Date.now(), - updatedAt: Date.now(), - nostrEventId: product.event_id, - // API-specific properties that are expected by the template - active: product.active ?? true, - pending: product.pending ?? false, - config: product.config || { currency: activeStall.value?.currency || 'sats' } - })) + // Convert API responses to domain models using clean mapping function + const enrichedProducts = (products || []).map(product => + mapApiResponseToProduct( + product, + activeStall.value?.name || 'Unknown Stall', + activeStall.value?.currency || 'sats' + ) + ) stallProducts.value = enrichedProducts // Only add active products to the market store so they appear in the main market diff --git a/src/modules/market/services/nostrmarketAPI.ts b/src/modules/market/services/nostrmarketAPI.ts index ea1ba55..77e56bf 100644 --- a/src/modules/market/services/nostrmarketAPI.ts +++ b/src/modules/market/services/nostrmarketAPI.ts @@ -68,7 +68,8 @@ export interface ProductConfig { shipping: ProductShippingCost[] } -export interface Product { +// API Response Types - Raw data from LNbits API +export interface ProductApiResponse { id?: string stall_id: string name: string @@ -358,8 +359,8 @@ export class NostrmarketAPI extends BaseService { /** * Get products for a stall */ - async getProducts(walletInkey: string, stallId: string, pending: boolean = false): Promise { - const products = await this.request( + async getProducts(walletInkey: string, stallId: string, pending: boolean = false): Promise { + const products = await this.request( `/api/v1/stall/product/${stallId}?pending=${pending}`, walletInkey, { method: 'GET' } @@ -380,8 +381,8 @@ export class NostrmarketAPI extends BaseService { async createProduct( walletAdminkey: string, productData: CreateProductRequest - ): Promise { - const product = await this.request( + ): Promise { + const product = await this.request( '/api/v1/product', walletAdminkey, { @@ -405,9 +406,9 @@ export class NostrmarketAPI extends BaseService { async updateProduct( walletAdminkey: string, productId: string, - productData: Product - ): Promise { - const product = await this.request( + productData: ProductApiResponse + ): Promise { + const product = await this.request( `/api/v1/product/${productId}`, walletAdminkey, { @@ -427,9 +428,9 @@ export class NostrmarketAPI extends BaseService { /** * Get a single product by ID */ - async getProduct(walletInkey: string, productId: string): Promise { + async getProduct(walletInkey: string, productId: string): Promise { try { - const product = await this.request( + const product = await this.request( `/api/v1/product/${productId}`, walletInkey, { method: 'GET' } diff --git a/src/modules/market/types/market.ts b/src/modules/market/types/market.ts index 253041e..5bfb290 100644 --- a/src/modules/market/types/market.ts +++ b/src/modules/market/types/market.ts @@ -26,6 +26,7 @@ export interface Stall { nostrEventId?: string } +// Domain Model - Single source of truth for Product export interface Product { id: string stall_id: string @@ -46,6 +47,71 @@ export interface Product { config?: { currency?: string, [key: string]: any } } +// Type aliases for API responses - imported from services +export type { ProductApiResponse, Stall as StallApiResponse } from '../services/nostrmarketAPI' + +// Mapping function to convert API response to domain model +export function mapApiResponseToProduct( + apiProduct: import('../services/nostrmarketAPI').ProductApiResponse, + stallName: string, + stallCurrency: string = 'sats' +): Product { + return { + id: apiProduct.id || `${apiProduct.stall_id}-${Date.now()}`, + stall_id: apiProduct.stall_id, + stallName, + name: apiProduct.name, + description: apiProduct.config?.description || '', + price: apiProduct.price, + currency: stallCurrency, + quantity: apiProduct.quantity, + images: apiProduct.images, + categories: apiProduct.categories, + createdAt: apiProduct.event_created_at || Date.now(), + updatedAt: Date.now(), + nostrEventId: apiProduct.event_id, + active: apiProduct.active, + pending: apiProduct.pending, + config: apiProduct.config + } +} + +// Mapping function to convert API response to domain model for Stall +export function mapApiResponseToStall( + apiStall: import('../services/nostrmarketAPI').Stall +): Stall { + return { + id: apiStall.id, + pubkey: '', // API Stall doesn't have pubkey, need to set from merchant context + name: apiStall.name, + description: apiStall.config?.description, + logo: apiStall.config?.image_url, + categories: [], // API Stall doesn't have categories + shipping: apiStall.shipping_zones?.map(zone => ({ + id: zone.id, + name: zone.name, + cost: zone.cost, + currency: apiStall.currency, + countries: zone.countries, + description: `${zone.name} shipping`, + estimatedDays: undefined, + requiresPhysicalShipping: true + })), + shipping_zones: apiStall.shipping_zones?.map(zone => ({ + id: zone.id, + name: zone.name, + cost: zone.cost, + currency: apiStall.currency, + countries: zone.countries, + description: `${zone.name} shipping`, + estimatedDays: undefined, + requiresPhysicalShipping: true + })), + currency: apiStall.currency, + nostrEventId: apiStall.event_id + } +} + export interface Order { id: string cartId: string