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