Skip to content

Commit 049c83d

Browse files
authored
Merge pull request #311 from Smartdevs17/feat-pricing-fixed
Feat: implement subscription price optimization engine
2 parents 3e3fd86 + 0c250ba commit 049c83d

4 files changed

Lines changed: 537 additions & 0 deletions

File tree

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
import React, { useState, useEffect } from 'react';
2+
import {
3+
View,
4+
Text,
5+
StyleSheet,
6+
ScrollView,
7+
TouchableOpacity,
8+
ActivityIndicator,
9+
} from 'react-native';
10+
import { colors, spacing } from '../../src/utils/constants';
11+
import { Card } from '../../src/components/common/Card';
12+
import { Button } from '../../src/components/common/Button';
13+
import {
14+
PricingService,
15+
PriceRecommendation,
16+
ABTestScenario,
17+
} from '../../backend/services/pricingService';
18+
19+
const PricingOptimizationScreen = () => {
20+
const [loading, setLoading] = useState(true);
21+
const [recommendation, setRecommendation] = useState<PriceRecommendation | null>(null);
22+
const [abTests, setAbTests] = useState<ABTestScenario[]>([]);
23+
const [activeTest, setActiveTest] = useState<string | null>(null);
24+
25+
useEffect(() => {
26+
fetchData();
27+
}, []);
28+
29+
const fetchData = async () => {
30+
setLoading(true);
31+
try {
32+
const rec = await PricingService.calculateOptimalPrice('sub_123', {
33+
current_price: 14.99,
34+
competitor_avg: 12.99,
35+
current_demand: 1.2,
36+
usage_data: {
37+
retention_rate: 0.85,
38+
sessions_per_week: 10,
39+
},
40+
});
41+
const tests = await PricingService.getPriceRecommendations('plan_gold');
42+
setRecommendation(rec);
43+
setAbTests(tests);
44+
} catch (error) {
45+
console.error(error);
46+
} finally {
47+
setLoading(false);
48+
}
49+
};
50+
51+
const renderMetric = (label: string, value: string | number, subtext?: string) => (
52+
<View style={styles.metricContainer}>
53+
<Text style={styles.metricLabel}>{label}</Text>
54+
<Text style={styles.metricValue}>{value}</Text>
55+
{subtext && <Text style={styles.metricSubtext}>{subtext}</Text>}
56+
</View>
57+
);
58+
59+
if (loading) {
60+
return (
61+
<View style={styles.loadingContainer}>
62+
<ActivityIndicator size="large" color={colors.primary} />
63+
</View>
64+
);
65+
}
66+
67+
return (
68+
<ScrollView style={styles.container}>
69+
<View style={styles.header}>
70+
<Text style={styles.title}>Pricing Optimization</Text>
71+
<Text style={styles.subtitle}>AI-Powered Revenue Maximization</Text>
72+
</View>
73+
74+
<Card style={styles.mainCard}>
75+
<Text style={styles.cardTitle}>Optimal Price Recommendation</Text>
76+
<View style={styles.priceRow}>
77+
<Text style={styles.currentPrice}>$14.99</Text>
78+
<Text style={styles.arrow}></Text>
79+
<Text style={styles.optimalPrice}>${recommendation?.optimalPrice}</Text>
80+
</View>
81+
<View
82+
style={[
83+
styles.badge,
84+
{
85+
backgroundColor:
86+
recommendation?.recommendation === 'Increase' ? '#E6F4EA' : '#FCE8E6',
87+
},
88+
]}>
89+
<Text
90+
style={[
91+
styles.badgeText,
92+
{ color: recommendation?.recommendation === 'Increase' ? '#1E8E3E' : '#D93025' },
93+
]}>
94+
{recommendation?.recommendation} Recommendation
95+
</Text>
96+
</View>
97+
98+
<View style={styles.factorsGrid}>
99+
{renderMetric('Demand Factor', `${recommendation?.factors.demandImpact}x`, 'High Demand')}
100+
{renderMetric(
101+
'Comp. Avg',
102+
`$${recommendation?.factors.competitorBenchmark}`,
103+
'Market Bench'
104+
)}
105+
{renderMetric('WTP Est.', `$${recommendation?.factors.willingnessToPay}`, 'User Value')}
106+
</View>
107+
</Card>
108+
109+
<View style={styles.section}>
110+
<Text style={styles.sectionTitle}>A/B Testing Strategies</Text>
111+
{abTests.map((test, index) => (
112+
<TouchableOpacity
113+
key={index}
114+
style={[styles.testCard, activeTest === test.tier && styles.activeTestCard]}
115+
onPress={() => setActiveTest(test.tier)}>
116+
<View style={styles.testHeader}>
117+
<Text style={styles.testTier}>{test.tier}</Text>
118+
<Text style={styles.testPrice}>${test.price}</Text>
119+
</View>
120+
<Text style={styles.testReasoning}>{test.reasoning}</Text>
121+
{activeTest === test.tier && (
122+
<View style={styles.activeBadge}>
123+
<Text style={styles.activeBadgeText}>ACTIVE TEST</Text>
124+
</View>
125+
)}
126+
</TouchableOpacity>
127+
))}
128+
</View>
129+
130+
<View style={styles.section}>
131+
<Text style={styles.sectionTitle}>Demand Forecast</Text>
132+
<Card style={styles.chartPlaceholder}>
133+
<View style={styles.barChart}>
134+
{[40, 60, 45, 90, 75, 80, 95].map((h, i) => (
135+
<View key={i} style={[styles.bar, { height: h }]} />
136+
))}
137+
</View>
138+
<View style={styles.chartLabels}>
139+
{['M', 'T', 'W', 'T', 'F', 'S', 'S'].map((l, i) => (
140+
<Text key={i} style={styles.chartLabel}>
141+
{l}
142+
</Text>
143+
))}
144+
</View>
145+
</Card>
146+
</View>
147+
148+
<Button title="Apply Recommended Pricing" onPress={() => {}} style={styles.applyButton} />
149+
<View style={{ height: 40 }} />
150+
</ScrollView>
151+
);
152+
};
153+
154+
const styles = StyleSheet.create({
155+
container: {
156+
flex: 1,
157+
backgroundColor: colors.background,
158+
},
159+
loadingContainer: {
160+
flex: 1,
161+
justifyContent: 'center',
162+
alignItems: 'center',
163+
},
164+
header: {
165+
padding: spacing.xl,
166+
paddingTop: 60,
167+
backgroundColor: colors.surface,
168+
},
169+
title: {
170+
fontSize: 28,
171+
fontWeight: 'bold',
172+
color: colors.text,
173+
},
174+
subtitle: {
175+
fontSize: 16,
176+
color: colors.textSecondary,
177+
marginTop: 4,
178+
},
179+
mainCard: {
180+
margin: spacing.lg,
181+
padding: spacing.xl,
182+
alignItems: 'center',
183+
},
184+
cardTitle: {
185+
fontSize: 18,
186+
fontWeight: '600',
187+
color: colors.textSecondary,
188+
marginBottom: spacing.lg,
189+
},
190+
priceRow: {
191+
flexDirection: 'row',
192+
alignItems: 'center',
193+
marginBottom: spacing.md,
194+
},
195+
currentPrice: {
196+
fontSize: 24,
197+
color: colors.textSecondary,
198+
textDecorationLine: 'line-through',
199+
},
200+
arrow: {
201+
fontSize: 24,
202+
marginHorizontal: spacing.md,
203+
color: colors.textSecondary,
204+
},
205+
optimalPrice: {
206+
fontSize: 36,
207+
fontWeight: 'bold',
208+
color: colors.primary,
209+
},
210+
badge: {
211+
paddingHorizontal: spacing.md,
212+
paddingVertical: spacing.xs,
213+
borderRadius: 12,
214+
marginBottom: spacing.xl,
215+
},
216+
badgeText: {
217+
fontSize: 12,
218+
fontWeight: 'bold',
219+
},
220+
factorsGrid: {
221+
flexDirection: 'row',
222+
justifyContent: 'space-between',
223+
width: '100%',
224+
borderTopWidth: 1,
225+
borderTopColor: colors.border,
226+
paddingTop: spacing.lg,
227+
},
228+
metricContainer: {
229+
alignItems: 'center',
230+
flex: 1,
231+
},
232+
metricLabel: {
233+
fontSize: 12,
234+
color: colors.textSecondary,
235+
marginBottom: 4,
236+
},
237+
metricValue: {
238+
fontSize: 18,
239+
fontWeight: 'bold',
240+
color: colors.text,
241+
},
242+
metricSubtext: {
243+
fontSize: 10,
244+
color: colors.textSecondary,
245+
marginTop: 2,
246+
},
247+
section: {
248+
paddingHorizontal: spacing.lg,
249+
marginBottom: spacing.xl,
250+
},
251+
sectionTitle: {
252+
fontSize: 18,
253+
fontWeight: 'bold',
254+
color: colors.text,
255+
marginBottom: spacing.md,
256+
},
257+
testCard: {
258+
backgroundColor: colors.surface,
259+
padding: spacing.lg,
260+
borderRadius: 12,
261+
marginBottom: spacing.md,
262+
borderWidth: 1,
263+
borderColor: colors.border,
264+
},
265+
activeTestCard: {
266+
borderColor: colors.primary,
267+
backgroundColor: '#F8F9FF',
268+
},
269+
testHeader: {
270+
flexDirection: 'row',
271+
justifyContent: 'space-between',
272+
alignItems: 'center',
273+
marginBottom: spacing.xs,
274+
},
275+
testTier: {
276+
fontSize: 16,
277+
fontWeight: 'bold',
278+
color: colors.text,
279+
},
280+
testPrice: {
281+
fontSize: 18,
282+
fontWeight: 'bold',
283+
color: colors.primary,
284+
},
285+
testReasoning: {
286+
fontSize: 14,
287+
color: colors.textSecondary,
288+
},
289+
activeBadge: {
290+
position: 'absolute',
291+
top: -8,
292+
right: 12,
293+
backgroundColor: colors.primary,
294+
paddingHorizontal: 8,
295+
paddingVertical: 2,
296+
borderRadius: 4,
297+
},
298+
activeBadgeText: {
299+
color: 'white',
300+
fontSize: 10,
301+
fontWeight: 'bold',
302+
},
303+
chartPlaceholder: {
304+
padding: spacing.lg,
305+
alignItems: 'center',
306+
},
307+
barChart: {
308+
flexDirection: 'row',
309+
alignItems: 'flex-end',
310+
justifyContent: 'space-around',
311+
width: '100%',
312+
height: 100,
313+
},
314+
bar: {
315+
width: 20,
316+
backgroundColor: colors.primary,
317+
borderRadius: 4,
318+
opacity: 0.7,
319+
},
320+
chartLabels: {
321+
flexDirection: 'row',
322+
justifyContent: 'space-around',
323+
width: '100%',
324+
marginTop: spacing.md,
325+
},
326+
chartLabel: {
327+
fontSize: 10,
328+
color: colors.textSecondary,
329+
},
330+
applyButton: {
331+
marginHorizontal: spacing.lg,
332+
marginTop: spacing.md,
333+
},
334+
});
335+
336+
export default PricingOptimizationScreen;

0 commit comments

Comments
 (0)