Skip to content

Commit d9ce71d

Browse files
committed
standardize ad behavior
1 parent 2e808ab commit d9ce71d

File tree

6 files changed

+349
-96
lines changed

6 files changed

+349
-96
lines changed

docker-compose.dev.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -246,23 +246,23 @@ services:
246246
com.datadoghq.ad.logs: '[{"source": "redis"}]'
247247

248248
# Browser automation for testing
249-
#puppeteer:
250-
# build:
251-
# context: ./services/puppeteer
252-
# platform: linux/amd64
253-
# environment:
254-
# - STOREDOG_URL=${STOREDOG_URL:-http://service-proxy:80}
255-
# - PUPPETEER_TIMEOUT=${PUPPETEER_TIMEOUT:-30000}
256-
# - SKIP_SESSION_CLOSE=${SKIP_SESSION_CLOSE:-false}
257-
# networks:
258-
# - storedog-network
259-
# volumes:
260-
# - ./services/puppeteer/scripts/puppeteer.js:/home/pptruser/puppeteer.js
261-
# depends_on:
262-
# - frontend
263-
# shm_size: '4gb' # Increase shared memory size
264-
# cap_add:
265-
# - 'SYS_ADMIN' # Required for Puppeteer to run in Docker
249+
puppeteer:
250+
build:
251+
context: ./services/puppeteer
252+
platform: linux/amd64
253+
environment:
254+
- STOREDOG_URL=${STOREDOG_URL:-http://service-proxy:80}
255+
- PUPPETEER_TIMEOUT=${PUPPETEER_TIMEOUT:-30000}
256+
- SKIP_SESSION_CLOSE=${SKIP_SESSION_CLOSE:-false}
257+
networks:
258+
- storedog-network
259+
volumes:
260+
- ./services/puppeteer/scripts/puppeteer.js:/home/pptruser/puppeteer.js
261+
depends_on:
262+
- frontend
263+
shm_size: '4gb' # Increase shared memory size
264+
cap_add:
265+
- 'SYS_ADMIN' # Required for Puppeteer to run in Docker
266266

267267
volumes:
268268
redis: # Redis data persistence

services/ads/java/src/main/java/adsjava/AdsJavaApplication.java

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,33 @@ public String home() {
4949
value = "/banners/{id}",
5050
produces = MediaType.IMAGE_JPEG_VALUE
5151
)
52-
public @ResponseBody byte[] getImageWithMediaType() throws IOException {
53-
logger.info("/banners/{id} called");
54-
int randomNum = ThreadLocalRandom.current().nextInt(1, 3 + 1);
55-
String imagePath = "/static/ads/ad" + randomNum + ".jpg";
56-
InputStream in = getClass()
57-
.getResourceAsStream(imagePath);
52+
public @ResponseBody byte[] getImageWithMediaType(@PathVariable String id) throws IOException {
53+
logger.info("/banners/{} called", id);
54+
55+
// Map the image path to the correct static file
56+
String imagePath;
57+
switch (id) {
58+
case "1.jpg":
59+
imagePath = "/static/ads/ad1.jpg";
60+
break;
61+
case "2.jpg":
62+
imagePath = "/static/ads/ad2.jpg";
63+
break;
64+
case "3.jpg":
65+
imagePath = "/static/ads/ad3.jpg";
66+
break;
67+
default:
68+
// Fallback to random image if unknown
69+
int randomNum = ThreadLocalRandom.current().nextInt(1, 3 + 1);
70+
imagePath = "/static/ads/ad" + randomNum + ".jpg";
71+
logger.warn("Unknown image id: {}, using random image", id);
72+
}
73+
74+
InputStream in = getClass().getResourceAsStream(imagePath);
75+
if (in == null) {
76+
logger.error("Image not found: {}", imagePath);
77+
throw new IOException("Image not found: " + imagePath);
78+
}
5879
return IOUtils.toByteArray(in);
5980
}
6081

@@ -132,9 +153,11 @@ public CommandLineRunner initDb(AdvertisementRepository repository) {
132153
return args -> {
133154
if (repository.count() == 0) {
134155
// Create ads with meaningful click URLs that point to relevant frontend pages
135-
repository.save(new Advertisement("Discount Clothing", "1.jpg", "/search?q=clothing"));
156+
// Based on actual image content, the files are mislabeled
157+
// Image 1.jpg shows Discount Clothing content, 2.jpg shows Cool Hats content
158+
repository.save(new Advertisement("Discount Clothing", "1.jpg", "/discount-clothing"));
136159
repository.save(new Advertisement("Cool Hats", "2.jpg", "/cool-hats"));
137-
repository.save(new Advertisement("Nice Bags", "3.jpg", "/search?q=bags"));
160+
repository.save(new Advertisement("Nice Bags", "3.jpg", "/nice-bags"));
138161
logger.info("Initialized database with 3 advertisements with click URLs");
139162
} else {
140163
// Always update existing ads to ensure they have the correct click URLs
@@ -145,24 +168,27 @@ public CommandLineRunner initDb(AdvertisementRepository repository) {
145168
String oldClickUrl = ad.getClickUrl();
146169
switch (ad.getName()) {
147170
case "Discount Clothing":
148-
if (!"/search?q=clothing".equals(oldClickUrl)) {
149-
ad.setClickUrl("/search?q=clothing");
171+
if (!"/discount-clothing".equals(oldClickUrl) || !"1.jpg".equals(ad.getPath())) {
172+
ad.setClickUrl("/discount-clothing");
173+
ad.setPath("1.jpg");
150174
needsUpdate = true;
151-
logger.info("Updated '{}' clickUrl from '{}' to '/search?q=clothing'", ad.getName(), oldClickUrl);
175+
logger.info("Updated '{}' clickUrl from '{}' to '/discount-clothing' and path to '1.jpg'", ad.getName(), oldClickUrl);
152176
}
153177
break;
154178
case "Cool Hats":
155-
if (!"/cool-hats".equals(oldClickUrl)) {
179+
if (!"/cool-hats".equals(oldClickUrl) || !"2.jpg".equals(ad.getPath())) {
156180
ad.setClickUrl("/cool-hats");
181+
ad.setPath("2.jpg");
157182
needsUpdate = true;
158-
logger.info("Updated '{}' clickUrl from '{}' to '/cool-hats'", ad.getName(), oldClickUrl);
183+
logger.info("Updated '{}' clickUrl from '{}' to '/cool-hats' and path to '2.jpg'", ad.getName(), oldClickUrl);
159184
}
160185
break;
161186
case "Nice Bags":
162-
if (!"/search?q=bags".equals(oldClickUrl)) {
163-
ad.setClickUrl("/search?q=bags");
187+
if (!"/nice-bags".equals(oldClickUrl) || !"3.jpg".equals(ad.getPath())) {
188+
ad.setClickUrl("/nice-bags");
189+
ad.setPath("3.jpg");
164190
needsUpdate = true;
165-
logger.info("Updated '{}' clickUrl from '{}' to '/search?q=bags'", ad.getName(), oldClickUrl);
191+
logger.info("Updated '{}' clickUrl from '{}' to '/nice-bags' and path to '3.jpg'", ad.getName(), oldClickUrl);
166192
}
167193
break;
168194
default:

services/frontend/components/common/Ad/Ad.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,23 @@ function Ad() {
3131

3232
try {
3333
console.log('ads path', adsPath)
34-
const res = await fetch(`${adsPath}/ads`, { headers })
34+
// Add cache-busting parameter to ensure fresh data
35+
const timestamp = Date.now()
36+
const res = await fetch(`${adsPath}/ads?t=${timestamp}`, { headers })
3537
if (!res.ok) {
3638
throw new Error('Error fetching ad')
3739
}
3840
const data: Advertisement[] = await res.json()
39-
console.log(data)
40-
const index = getRandomArbitrary(0, data.length)
41-
setData(data[index])
41+
console.log('Available ads:', data)
42+
// Sort ads by ID to ensure consistent ordering
43+
const sortedAds = data.sort((a, b) => a.id - b.id)
44+
// Use a deterministic selection based on time to show different ads
45+
// This ensures the visual ad matches the expected click behavior
46+
const now = new Date()
47+
const adIndex = Math.floor(now.getSeconds() / 5) % sortedAds.length // Change ad every 5 seconds
48+
const selectedAd = sortedAds[adIndex]
49+
console.log('Selected ad:', selectedAd)
50+
setData(selectedAd)
4251
setLoading(false)
4352
} catch (e) {
4453
console.error(e)
@@ -48,6 +57,13 @@ function Ad() {
4857

4958
const handleAdClick = useCallback(() => {
5059
if (data?.id) {
60+
console.log('Ad clicked!', {
61+
adId: data.id,
62+
adName: data.name,
63+
clickUrl: data.clickUrl,
64+
imagePath: data.path,
65+
redirectUrl: `${adsPath}/click/${data.id}`
66+
})
5167
// Direct browser navigation to the click endpoint
5268
// The Java service will handle the redirect to the appropriate URL
5369
window.location.href = `${adsPath}/click/${data.id}`

services/frontend/pages/cool-hats.tsx

Lines changed: 20 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -64,68 +64,29 @@ export default function CoolHatsPage({ hatProducts }: CoolHatsPageProps) {
6464
</Text>
6565
</div>
6666

67-
{/* Success Message */}
68-
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-8">
69-
<div className="flex items-center">
70-
<div className="flex-shrink-0">
71-
<svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
72-
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
73-
</svg>
74-
</div>
75-
<div className="ml-3">
76-
<h3 className="text-sm font-medium text-green-800">
77-
🎉 Ad Click Success!
78-
</h3>
79-
<div className="mt-2 text-sm text-green-700">
80-
<p>
81-
Great news! The ad click functionality is working perfectly.
82-
You successfully clicked on the "Cool Hats" advertisement and were redirected here.
83-
</p>
84-
</div>
85-
</div>
86-
</div>
87-
</div>
8867

89-
{/* Hat Products Section */}
90-
{hatProducts.length > 0 ? (
91-
<>
92-
<h2 className="text-2xl font-bold text-gray-900 mb-6">Featured Hat Products</h2>
93-
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
94-
{hatProducts.map((product) => (
95-
<ProductCard
96-
key={product.id}
97-
product={product}
98-
variant="simple"
99-
imgProps={{
100-
width: 300,
101-
height: 300,
102-
}}
103-
/>
104-
))}
105-
</div>
106-
</>
107-
) : (
108-
<div className="text-center py-12">
109-
<div className="text-6xl mb-4">🎩</div>
110-
<h2 className="text-2xl font-bold text-gray-900 mb-2">No Hat Products Found</h2>
111-
<Text className="text-gray-600">
112-
We're still building our hat collection! Check back soon for awesome headwear.
113-
</Text>
114-
</div>
115-
)}
11668

117-
{/* Call to Action */}
118-
<div className="mt-16 text-center">
119-
<div className="bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg p-8 text-white">
120-
<h2 className="text-3xl font-bold mb-4">Love Hats? We've Got You Covered!</h2>
121-
<Text className="text-xl mb-6 opacity-90">
122-
Join our hat enthusiasts community and be the first to know about new arrivals.
123-
</Text>
124-
<button className="bg-white text-blue-600 font-bold py-3 px-8 rounded-lg hover:bg-gray-100 transition-colors">
125-
Explore All Products
126-
</button>
127-
</div>
69+
{/* Hat Products Section */}
70+
<div className="text-center py-12">
71+
<div className="text-6xl mb-4">🧢</div>
72+
<h2 className="text-2xl font-bold text-gray-900 mb-2">No Hat Products Found</h2>
73+
<Text className="text-gray-600">
74+
We're still building our hat collection! Check back soon for awesome headwear.
75+
</Text>
12876
</div>
77+
78+
{/* Call to Action */}
79+
<div className="mt-16 text-center">
80+
<div className="bg-purple-900 rounded-lg p-8">
81+
<h2 className="text-3xl font-bold mb-4 text-white">Love Fashion? We've Got You Covered!</h2>
82+
<Text className="text-xl mb-6 text-white opacity-90">
83+
Join our hat enthusiasts community and be the first to know about new arrivals.
84+
</Text>
85+
<a href="/" className="inline-block bg-white text-purple-800 font-bold py-3 px-8 rounded-lg hover:bg-gray-100 transition-colors">
86+
Explore All Products
87+
</a>
88+
</div>
89+
</div>
12990
</Container>
13091
</>
13192
)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { Layout } from '@components/common'
2+
import { Container, Text } from '@components/ui'
3+
import { SEO } from '@components/common'
4+
import { GetServerSidePropsContext } from 'next'
5+
import { Product } from '@customTypes/product'
6+
import { ProductCard } from '@components/product'
7+
8+
interface DiscountClothingPageProps {
9+
clothingProducts: Product[]
10+
}
11+
12+
export async function getServerSideProps(context: GetServerSidePropsContext) {
13+
const baseUrl = process.env.NEXT_PUBLIC_FRONTEND_API_ROUTE
14+
? `${process.env.NEXT_PUBLIC_FRONTEND_API_ROUTE}/api`
15+
: 'http://localhost:3000/api'
16+
17+
try {
18+
// Fetch all products and filter for clothing-related items
19+
const allProducts: Product[] = await fetch(`${baseUrl}/products`)
20+
.then((res) => res.json())
21+
.catch(() => [])
22+
23+
// Filter for clothing-related products
24+
const clothingProducts = allProducts.filter(product =>
25+
product.name?.toLowerCase().includes('shirt') ||
26+
product.name?.toLowerCase().includes('dress') ||
27+
product.name?.toLowerCase().includes('pants') ||
28+
product.name?.toLowerCase().includes('jeans') ||
29+
product.name?.toLowerCase().includes('sweater') ||
30+
product.name?.toLowerCase().includes('jacket') ||
31+
product.name?.toLowerCase().includes('coat') ||
32+
product.name?.toLowerCase().includes('blouse') ||
33+
product.name?.toLowerCase().includes('skirt') ||
34+
product.name?.toLowerCase().includes('t-shirt') ||
35+
product.name?.toLowerCase().includes('hoodie') ||
36+
product.description?.toLowerCase().includes('shirt') ||
37+
product.description?.toLowerCase().includes('dress') ||
38+
product.description?.toLowerCase().includes('pants') ||
39+
product.description?.toLowerCase().includes('jeans') ||
40+
product.description?.toLowerCase().includes('sweater') ||
41+
product.description?.toLowerCase().includes('jacket') ||
42+
product.description?.toLowerCase().includes('coat') ||
43+
product.description?.toLowerCase().includes('blouse') ||
44+
product.description?.toLowerCase().includes('skirt') ||
45+
product.description?.toLowerCase().includes('t-shirt') ||
46+
product.description?.toLowerCase().includes('hoodie')
47+
)
48+
49+
return {
50+
props: {
51+
clothingProducts: clothingProducts.slice(0, 12) // Limit to 12 products
52+
}
53+
}
54+
} catch (error) {
55+
console.error('Error fetching clothing products:', error)
56+
return {
57+
props: {
58+
clothingProducts: []
59+
}
60+
}
61+
}
62+
}
63+
64+
export default function DiscountClothingPage({ clothingProducts }: DiscountClothingPageProps) {
65+
return (
66+
<>
67+
<SEO
68+
title="Discount Clothing - Storedog"
69+
description="Discover our amazing collection of discount clothing and fashion items. Style meets affordability in our curated selection."
70+
/>
71+
72+
<Container className="pt-8 pb-16">
73+
{/* Hero Section */}
74+
<div className="text-center mb-12">
75+
<h1 className="text-4xl md:text-6xl font-bold text-gray-900 mb-4">
76+
👕 Discount Clothing Collection
77+
</h1>
78+
<Text className="text-xl text-gray-600 max-w-2xl mx-auto">
79+
Welcome to our fabulous discount clothing collection! You clicked on the Discount Clothing ad and landed here.
80+
Discover stylish fashion at unbeatable prices.
81+
</Text>
82+
</div>
83+
84+
85+
86+
{/* Clothing Products Section */}
87+
{clothingProducts.length > 0 ? (
88+
<>
89+
<h2 className="text-2xl font-bold text-gray-900 mb-6">Featured Clothing Products</h2>
90+
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
91+
{clothingProducts.map((product) => (
92+
<ProductCard
93+
key={product.id}
94+
product={product}
95+
variant="simple"
96+
imgProps={{
97+
width: 300,
98+
height: 300,
99+
}}
100+
/>
101+
))}
102+
</div>
103+
</>
104+
) : (
105+
<div className="text-center py-12">
106+
<div className="text-6xl mb-4">👕</div>
107+
<h2 className="text-2xl font-bold text-gray-900 mb-2">No Clothing Products Found</h2>
108+
<Text className="text-gray-600">
109+
We're still building our clothing collection! Check back soon for fabulous fashion items.
110+
</Text>
111+
</div>
112+
)}
113+
114+
{/* Call to Action */}
115+
<div className="mt-16 text-center">
116+
<div className="bg-purple-900 rounded-lg p-8">
117+
<h2 className="text-3xl font-bold mb-4 text-white">Love Fashion? We've Got You Covered!</h2>
118+
<Text className="text-xl mb-6 text-white opacity-90">
119+
Join our fashion-forward community and be the first to know about new arrivals and exclusive discounts.
120+
</Text>
121+
<a href="/" className="inline-block bg-white text-purple-800 font-bold py-3 px-8 rounded-lg hover:bg-gray-100 transition-colors">
122+
Explore All Products
123+
</a>
124+
</div>
125+
</div>
126+
</Container>
127+
</>
128+
)
129+
}
130+
131+
DiscountClothingPage.Layout = Layout

0 commit comments

Comments
 (0)