Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e571ce6
feat: Add username field to user registration and profile management
CDevmina May 7, 2025
7025993
feat: Add nickname update for store profile and enhance metadata hand…
CDevmina May 7, 2025
e67e169
Username fix
CDevmina May 7, 2025
c2e3aba
refactor: Remove unused navigation state from RegistrationCompletionM…
CDevmina May 7, 2025
0da4ecb
refactor: Remove linkAccounts function and clean up user registration…
CDevmina May 8, 2025
fb6c882
feat: Add OpenAPI specification for Tapiro Store Operations API
CDevmina May 8, 2025
9f7c528
feat: Add new product images and remove obsolete video streaming subs…
CDevmina May 8, 2025
d2bf8d5
feat: Update product images to use actual images instead of placeholders
CDevmina May 8, 2025
d64a264
feat: Add link to demo app in the header navigation
CDevmina May 8, 2025
01e155c
Demo recommendation fix
CDevmina May 13, 2025
0dced98
Temp: Cors fix
CDevmina May 13, 2025
f25c01e
feat: Implement Auth0 utility functions for user management and caching
CDevmina May 13, 2025
5fb1ed1
feat: Enhance API health check and CORS configuration; update service…
CDevmina May 13, 2025
f0d8d1c
Implement code changes to enhance functionality and improve performance
CDevmina May 14, 2025
2352ff4
feat: Add entriesHash to cache key for user data processing
CDevmina May 14, 2025
648edc2
feat: Enhance API key validation and caching; implement fire-and-forg…
CDevmina May 14, 2025
3714c75
feat: Refactor user inference function to accept user object and taxo…
CDevmina May 14, 2025
81497eb
feat: Update API generation script path and remove unused user data m…
CDevmina May 14, 2025
39d9f57
feat: Enhance UserAnalyticsPage table structure and add clear date fi…
CDevmina May 14, 2025
13d1fb5
refactor: Rename lookupStores to searchStores for consistency in Stor…
CDevmina May 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 57 additions & 19 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
services:
api-service:
tapiro-api-internal:
build:
context: ./api-service
context: ./tapiro-api-internal
dockerfile: dockerfile
target: development
container_name: tapiro-API
container_name: tapiro-api-internal
volumes:
- ./api-service:/app
- ./tapiro-api-internal:/app
- /app/node_modules
environment:
- NODE_ENV=development
Expand All @@ -30,6 +30,7 @@ services:
- AI_SERVICE_URL=http://ml-service:8000/api
- AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
- AUTH0_DOMAIN=${AUTH0_DOMAIN}
- EXTERNAL_API_URL=http://tapiro-api-external:3001
ports:
- "3000:3000"
depends_on:
Expand All @@ -44,6 +45,41 @@ services:
retries: 3
start_period: 15s

tapiro-api-external:
build:
context: ./tapiro-api-external
dockerfile: dockerfile
target: development
container_name: tapiro-api-external
volumes:
- ./tapiro-api-external:/app
- /app/node_modules
environment:
- NODE_ENV=development
- MONGODB_URI=${MONGODB_URI}
- DB_NAME=tapiro
- REDIS_HOST=redis
- REDIS_PORT=6379
- PORT=3001
- AI_SERVICE_URL=http://ml-service:8000/api
- AI_SERVICE_API_KEY=${AI_SERVICE_API_KEY}
- ALLOWED_ORIGINS=http://localhost:5174
ports:
- "3001:3001"
depends_on:
redis:
condition: service_healthy
tapiro-api-internal:
condition: service_started
networks:
- tapiro-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s

ml-service:
build:
context: ./ml-service
Expand All @@ -55,9 +91,9 @@ services:
- ml-model-cache:/app/model_cache
environment:
- MONGODB_URI=${MONGODB_URI}
- API_BASE_URL=http://api-service:3000
- API_BASE_URL=http://tapiro-api-internal:3000
- EXTERNAL_API_URL=http://tapiro-api-external:3001
- MONGODB_DB_NAME=tapiro
- BACKEND_API_URL=http://api-service:3000
- SECRET_KEY=${AI_SERVICE_API_KEY}
- DEBUG=true
- REDIS_HOST=redis
Expand Down Expand Up @@ -97,14 +133,28 @@ services:
networks:
- tapiro-net
depends_on:
- api-service
- tapiro-api-internal
healthcheck:
test: ["CMD", "wget", "-O", "/dev/null", "http://localhost:5173"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s

demo-store:
build:
context: ./demo
dockerfile: Dockerfile
container_name: demo
environment:
- VITE_STORE_API_URL=http://localhost:3001
ports:
- "5174:80"
networks:
- tapiro-net
depends_on:
- tapiro-api-external

redis:
image: redis:alpine
container_name: tapiro-redis
Expand All @@ -122,18 +172,6 @@ services:
retries: 5
start_period: 10s

demo-store:
build:
context: ./demo
dockerfile: Dockerfile
container_name: demo
ports:
- "5174:80"
networks:
- tapiro-net
depends_on:
- api-service

networks:
tapiro-net:

Expand Down
Binary file added demo/public/products/apples.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/babystroller.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/boots.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/buildingblocks.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/cleanser.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/coffee.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/dell-laptop.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/dress.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/drill.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/fictionnoval.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/gamgingmouse.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/gaming-laptop.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/garden-tools.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/gardening.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/genpens.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/giftbasket.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/headphones.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/indiegame.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/ipad.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/iphonex.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/jeans.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/kidstshirt.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/lipstick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/mattfoundation.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/necklace.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/public/products/productivitysuite.webp
Binary file added demo/public/products/samsung.webp
Binary file added demo/public/products/sciencefiction.webp
Binary file added demo/public/products/smartwatch.webp
Binary file added demo/public/products/sneakers.jpg.webp
Binary file added demo/public/products/sofa.webp
Binary file added demo/public/products/steelpan.webp
Binary file added demo/public/products/suit.webp
Binary file added demo/public/products/suitecase.webp
Binary file added demo/public/products/tabel.webp
Binary file added demo/public/products/toothbrush.webp
Binary file added demo/public/products/tshirt.webp
Binary file added demo/public/products/tuna.webp
Binary file added demo/public/products/vitamin.webp
Binary file added demo/public/products/vitaminc.webp
123 changes: 94 additions & 29 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ function App() {
);

// Get API details from environment variables (only URL now)
const apiUrl = import.meta.env.VITE_TAPIRO_API_URL;
// const apiKey = import.meta.env.VITE_STORE_API_KEY; // Removed: Get from state
const apiUrl = import.meta.env.VITE_STORE_API_URL || "http://localhost:3001";

// --- Effects ---
useEffect(() => {
Expand Down Expand Up @@ -90,27 +89,48 @@ function App() {

// Sort by preference score if preferences are loaded
if (preferences && preferences.length > 0) {
const prefMap = new Map(
preferences.map((p) => [p.category, p.score ?? 0])
);
const getScore = (product: Product): number => {
// Basic score: category match
let score = prefMap.get(product.categoryId) ?? 0;

// Bonus for attribute matches (simple example)
if (product.attributes && prefMap.has(product.categoryId)) {
const categoryPrefs = preferences.find(
(p) => p.category === product.categoryId
);
if (categoryPrefs?.attributes) {
for (const attrKey in product.attributes) {
if (
categoryPrefs.attributes[attrKey]?.[product.attributes[attrKey]]
) {
score += 0.1; // Small bonus for each matching attribute value
const categoryPreference = preferences.find(
(p) => p.category === product.categoryId
);

if (!categoryPreference) {
return 0; // No preference for this category
}

let score = categoryPreference.score; // Base score from category

// Add bonus for attribute matches
if (product.attributes && categoryPreference.attributes) {
let attributeBonus = 0;
let matchingAttributes = 0;
const prefAttributes = categoryPreference.attributes;

for (const attrKey in product.attributes) {
const productAttrValue = String(
product.attributes[attrKey]
).toLowerCase();
if (prefAttributes[attrKey]) {
// Check if the specific product attribute value has a score
if (prefAttributes[attrKey][productAttrValue]) {
attributeBonus += prefAttributes[attrKey][productAttrValue]; // Add the specific attribute value's score
matchingAttributes++;
} else {
// If the exact value isn't scored, check if the attribute key itself has a general preference
// This handles cases where preference is for "brand: Apple" but product has "brand: Apple, color: Red"
// and "brand" itself might have a score in preferences if not specific value.
// For simplicity, let's assume if the attribute key (e.g., "brand") exists in preferences, it's a partial match.
// A more complex logic could assign a smaller bonus here.
// For now, we only reward exact value matches from prefAttributes.
}
}
}
// Normalize bonus by number of matching attributes to avoid overly penalizing products with many attributes
if (matchingAttributes > 0) {
// Example: Add average bonus of matched attributes.
// You might want a different logic, e.g., sum of bonuses, or cap the bonus.
score += attributeBonus / matchingAttributes;
}
}
return score;
};
Expand Down Expand Up @@ -334,20 +354,65 @@ function App() {
// --- Recommended Product IDs ---
const recommendedProductIds = useMemo(() => {
const ids = new Set<string>();
if (preferences && preferences.length > 0) {
const prefMap = new Map(
preferences.map((p) => [p.category, p.score ?? 0])
);
displayedProducts.forEach((p) => {
const score = prefMap.get(p.categoryId) ?? 0;
if (score > 0.5) {
// Example threshold for "recommended"
ids.add(p.id);
if (preferences && preferences.length > 0 && displayedProducts.length > 0) {
// Get scores for all currently displayed products
const productScores = displayedProducts.map((product) => {
const categoryPreference = preferences.find(
(p) => p.category === product.categoryId
);
if (!categoryPreference) return { id: product.id, score: 0 };

let score = categoryPreference.score;
if (product.attributes && categoryPreference.attributes) {
let attributeBonus = 0;
let matchingAttributes = 0;
const prefAttributes = categoryPreference.attributes;
for (const attrKey in product.attributes) {
const productAttrValue = String(
product.attributes[attrKey]
).toLowerCase();
if (
prefAttributes[attrKey] &&
prefAttributes[attrKey][productAttrValue]
) {
attributeBonus += prefAttributes[attrKey][productAttrValue];
matchingAttributes++;
}
}
if (matchingAttributes > 0) {
score += attributeBonus / matchingAttributes; // Average bonus
}
}
return { id: product.id, score };
});

// Sort products by score to find the top ones
productScores.sort((a, b) => b.score - a.score);

// Recommend top N products or products above a certain score threshold
// For example, recommend products with a score > 0.6 (adjust as needed)
// Or recommend the top 3-5 products if there are enough.
const recommendationThreshold = 0.6; // Adjusted threshold
productScores.forEach((ps) => {
if (ps.score > recommendationThreshold) {
ids.add(ps.id);
}
});

// If no products are above the threshold, maybe recommend the top 1-2 anyway if scores are positive
if (
ids.size === 0 &&
productScores.length > 0 &&
productScores[0].score > 0.1
) {
ids.add(productScores[0].id);
if (productScores.length > 1 && productScores[1].score > 0.1) {
ids.add(productScores[1].id);
}
}
}
return ids;
}, [preferences, displayedProducts]);
}, [preferences, displayedProducts]); // Recalculate when preferences or displayed products change

// Helper to display API Key (show prefix only for brevity/security)
const displayApiKey = apiKey ? `${apiKey.substring(0, 8)}...` : "Not Set";
Expand Down
Loading
Loading