@playbasis-ai/qwikcard-sdk 2.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -0
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/SDK_HANDOVER_GUIDE.md +129 -0
- package/dist/PlaybasisProvider.d.ts +19 -0
- package/dist/PlaybasisProvider.d.ts.map +1 -0
- package/dist/PlaybasisProvider.js +24 -0
- package/dist/QwikCardApp.d.ts +9 -0
- package/dist/QwikCardApp.d.ts.map +1 -0
- package/dist/QwikCardApp.js +210 -0
- package/dist/api/client.d.ts +66 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +196 -0
- package/dist/components/Badge.d.ts +8 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +34 -0
- package/dist/components/BadgeIcon.d.ts +10 -0
- package/dist/components/BadgeIcon.d.ts.map +1 -0
- package/dist/components/BadgeIcon.js +51 -0
- package/dist/components/Button.d.ts +10 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +40 -0
- package/dist/components/GradientCard.d.ts +9 -0
- package/dist/components/GradientCard.d.ts.map +1 -0
- package/dist/components/GradientCard.js +28 -0
- package/dist/components/PointsBalance.d.ts +8 -0
- package/dist/components/PointsBalance.d.ts.map +1 -0
- package/dist/components/PointsBalance.js +85 -0
- package/dist/components/ProgressBar.d.ts +8 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +41 -0
- package/dist/components/QuestProgress.d.ts +10 -0
- package/dist/components/QuestProgress.d.ts.map +1 -0
- package/dist/components/QuestProgress.js +94 -0
- package/dist/components/RadialGauge.d.ts +8 -0
- package/dist/components/RadialGauge.d.ts.map +1 -0
- package/dist/components/RadialGauge.js +53 -0
- package/dist/components/RewardCard.d.ts +10 -0
- package/dist/components/RewardCard.d.ts.map +1 -0
- package/dist/components/RewardCard.js +64 -0
- package/dist/components/RulesModal.d.ts +7 -0
- package/dist/components/RulesModal.d.ts.map +1 -0
- package/dist/components/RulesModal.js +106 -0
- package/dist/hooks/useBadges.d.ts +12 -0
- package/dist/hooks/useBadges.d.ts.map +1 -0
- package/dist/hooks/useBadges.js +42 -0
- package/dist/hooks/usePoints.d.ts +13 -0
- package/dist/hooks/usePoints.d.ts.map +1 -0
- package/dist/hooks/usePoints.js +70 -0
- package/dist/hooks/useQuests.d.ts +12 -0
- package/dist/hooks/useQuests.d.ts.map +1 -0
- package/dist/hooks/useQuests.js +41 -0
- package/dist/hooks/useQwikApp.d.ts +16 -0
- package/dist/hooks/useQwikApp.d.ts.map +1 -0
- package/dist/hooks/useQwikApp.js +42 -0
- package/dist/hooks/useRewards.d.ts +12 -0
- package/dist/hooks/useRewards.d.ts.map +1 -0
- package/dist/hooks/useRewards.js +56 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/theme/context.d.ts +8 -0
- package/dist/theme/context.d.ts.map +1 -0
- package/dist/theme/context.js +8 -0
- package/dist/theme/tokens.d.ts +35 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +33 -0
- package/dist/types/index.d.ts +119 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/web/widgetAssets.d.ts +4 -0
- package/dist/web/widgetAssets.d.ts.map +1 -0
- package/dist/web/widgetAssets.js +5 -0
- package/dist/web/widgetHtml.d.ts +2 -0
- package/dist/web/widgetHtml.d.ts.map +1 -0
- package/dist/web/widgetHtml.js +299 -0
- package/dist/web/widgetTypes.d.ts +128 -0
- package/dist/web/widgetTypes.d.ts.map +1 -0
- package/dist/web/widgetTypes.js +1 -0
- package/package.json +86 -0
- package/src/PlaybasisProvider.tsx +72 -0
- package/src/QwikCardApp.tsx +302 -0
- package/src/api/client.ts +307 -0
- package/src/components/Badge.tsx +51 -0
- package/src/components/BadgeIcon.tsx +97 -0
- package/src/components/Button.tsx +70 -0
- package/src/components/GradientCard.tsx +49 -0
- package/src/components/PointsBalance.tsx +122 -0
- package/src/components/ProgressBar.tsx +65 -0
- package/src/components/QuestProgress.tsx +153 -0
- package/src/components/RadialGauge.tsx +101 -0
- package/src/components/RewardCard.tsx +123 -0
- package/src/components/RulesModal.tsx +171 -0
- package/src/hooks/useBadges.ts +59 -0
- package/src/hooks/usePoints.ts +91 -0
- package/src/hooks/useQuests.ts +60 -0
- package/src/hooks/useQwikApp.ts +49 -0
- package/src/hooks/useRewards.ts +74 -0
- package/src/index.ts +34 -0
- package/src/theme/context.tsx +17 -0
- package/src/theme/tokens.ts +68 -0
- package/src/types/index.ts +176 -0
- package/src/web/widgetAssets.d.ts +3 -0
- package/src/web/widgetAssets.ts +6 -0
- package/src/web/widgetHtml.ts +302 -0
- package/src/web/widgetTypes.ts +146 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GradientCard.d.ts","sourceRoot":"","sources":["../../src/components/GradientCard.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAoB,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKtE,UAAU,iBAAiB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CA8BlE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
4
|
+
import { useTheme } from '../theme/context';
|
|
5
|
+
export function GradientCard({ children, style }) {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
const isGradientAvailable = LinearGradient !== undefined &&
|
|
8
|
+
LinearGradient !== null &&
|
|
9
|
+
(typeof LinearGradient === 'function' || typeof LinearGradient === 'object');
|
|
10
|
+
const containerStyle = [
|
|
11
|
+
styles.container,
|
|
12
|
+
{
|
|
13
|
+
borderRadius: theme.borderRadius.l,
|
|
14
|
+
borderColor: theme.colors.surface,
|
|
15
|
+
},
|
|
16
|
+
style,
|
|
17
|
+
];
|
|
18
|
+
if (!isGradientAvailable) {
|
|
19
|
+
return _jsx(View, { style: containerStyle, children: children });
|
|
20
|
+
}
|
|
21
|
+
return (_jsx(LinearGradient, { colors: ['rgba(255, 255, 255, 0.12)', 'rgba(255, 255, 255, 0.04)'], start: { x: 0, y: 0 }, end: { x: 1, y: 1 }, style: containerStyle, children: children }));
|
|
22
|
+
}
|
|
23
|
+
const styles = StyleSheet.create({
|
|
24
|
+
container: {
|
|
25
|
+
borderWidth: 1,
|
|
26
|
+
overflow: 'hidden',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PointBalance } from '../types';
|
|
2
|
+
interface PointsBalanceProps {
|
|
3
|
+
balances: PointBalance[];
|
|
4
|
+
style?: object;
|
|
5
|
+
}
|
|
6
|
+
export declare function PointsBalance({ balances, style }: PointsBalanceProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default PointsBalance;
|
|
8
|
+
//# sourceMappingURL=PointsBalance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PointsBalance.d.ts","sourceRoot":"","sources":["../../src/components/PointsBalance.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAqCpE;AAwED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import { GradientCard } from './GradientCard';
|
|
4
|
+
import { useTheme } from '../theme/context';
|
|
5
|
+
export function PointsBalance({ balances, style }) {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
// Find primary and secondary currencies
|
|
8
|
+
const primary = balances.find((b) => b.currency === 'qwik-coins') || balances[0];
|
|
9
|
+
const secondary = balances.find((b) => b.currency !== primary?.currency);
|
|
10
|
+
const formattedBalance = primary?.balance.toLocaleString() || '0';
|
|
11
|
+
const secondaryBalance = secondary
|
|
12
|
+
? `${secondary.balance.toLocaleString()} ${secondary.currency}`
|
|
13
|
+
: null;
|
|
14
|
+
return (_jsxs(GradientCard, { style: [styles.card, style], children: [_jsxs(View, { style: styles.header, children: [_jsx(Text, { style: styles.label, children: "CURRENT BALANCE" }), _jsx(Text, { style: styles.brand, children: "QwikCard" })] }), _jsxs(View, { style: styles.mainContent, children: [_jsx(Text, { style: styles.currencySymbol, children: "$" }), _jsx(Text, { style: styles.amount, children: formattedBalance })] }), _jsxs(View, { style: styles.footer, children: [secondaryBalance && (_jsx(View, { style: styles.chip, children: _jsxs(Text, { style: styles.chipText, children: ["+ ", secondaryBalance] }) })), _jsxs(View, { style: styles.dots, children: [_jsx(View, { style: [styles.dot, { backgroundColor: '#FFD700' }] }), _jsx(View, { style: [styles.dot, { backgroundColor: '#FF69B4' }] })] })] })] }));
|
|
15
|
+
}
|
|
16
|
+
const styles = StyleSheet.create({
|
|
17
|
+
card: {
|
|
18
|
+
padding: 20,
|
|
19
|
+
minHeight: 150,
|
|
20
|
+
justifyContent: 'space-between',
|
|
21
|
+
},
|
|
22
|
+
header: {
|
|
23
|
+
flexDirection: 'row',
|
|
24
|
+
justifyContent: 'space-between',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
marginBottom: 16,
|
|
27
|
+
},
|
|
28
|
+
label: {
|
|
29
|
+
color: 'rgba(255, 255, 255, 0.8)',
|
|
30
|
+
fontSize: 10,
|
|
31
|
+
fontWeight: '700',
|
|
32
|
+
letterSpacing: 1.2,
|
|
33
|
+
},
|
|
34
|
+
brand: {
|
|
35
|
+
color: '#FFFFFF',
|
|
36
|
+
fontSize: 12,
|
|
37
|
+
fontWeight: '800',
|
|
38
|
+
fontStyle: 'italic',
|
|
39
|
+
},
|
|
40
|
+
mainContent: {
|
|
41
|
+
flexDirection: 'row',
|
|
42
|
+
alignItems: 'flex-start',
|
|
43
|
+
},
|
|
44
|
+
currencySymbol: {
|
|
45
|
+
color: 'rgba(255, 255, 255, 0.9)',
|
|
46
|
+
fontSize: 20,
|
|
47
|
+
fontWeight: '600',
|
|
48
|
+
marginTop: 2,
|
|
49
|
+
marginRight: 4,
|
|
50
|
+
},
|
|
51
|
+
amount: {
|
|
52
|
+
color: '#FFFFFF',
|
|
53
|
+
fontSize: 32,
|
|
54
|
+
fontWeight: '800',
|
|
55
|
+
letterSpacing: -0.5,
|
|
56
|
+
},
|
|
57
|
+
footer: {
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
justifyContent: 'space-between',
|
|
60
|
+
alignItems: 'center',
|
|
61
|
+
marginTop: 16,
|
|
62
|
+
},
|
|
63
|
+
chip: {
|
|
64
|
+
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
65
|
+
paddingHorizontal: 10,
|
|
66
|
+
paddingVertical: 4,
|
|
67
|
+
borderRadius: 16,
|
|
68
|
+
},
|
|
69
|
+
chipText: {
|
|
70
|
+
color: '#FFFFFF',
|
|
71
|
+
fontSize: 11,
|
|
72
|
+
fontWeight: '600',
|
|
73
|
+
},
|
|
74
|
+
dots: {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
gap: 5,
|
|
77
|
+
},
|
|
78
|
+
dot: {
|
|
79
|
+
width: 20,
|
|
80
|
+
height: 20,
|
|
81
|
+
borderRadius: 10,
|
|
82
|
+
opacity: 0.8,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
export default PointsBalance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProgressBar.d.ts","sourceRoot":"","sources":["../../src/components/ProgressBar.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,gBAAgB,2CAyBpE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import { useTheme } from '../theme/context';
|
|
4
|
+
export function ProgressBar({ label, value, color }) {
|
|
5
|
+
const theme = useTheme();
|
|
6
|
+
const barColor = color || theme.colors.primary;
|
|
7
|
+
return (_jsxs(View, { style: styles.container, children: [label && (_jsxs(View, { style: styles.header, children: [_jsx(Text, { style: [styles.label, { color: theme.colors.text.primary }], children: label }), _jsxs(Text, { style: [styles.value, { color: theme.colors.text.secondary }], children: [value, "%"] })] })), _jsx(View, { style: [styles.track, { backgroundColor: 'rgba(148, 163, 184, 0.2)' }], children: _jsx(View, { style: [
|
|
8
|
+
styles.fill,
|
|
9
|
+
{
|
|
10
|
+
width: `${value}%`,
|
|
11
|
+
backgroundColor: barColor,
|
|
12
|
+
},
|
|
13
|
+
] }) })] }));
|
|
14
|
+
}
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
container: {
|
|
17
|
+
width: '100%',
|
|
18
|
+
marginVertical: 4,
|
|
19
|
+
},
|
|
20
|
+
header: {
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
justifyContent: 'space-between',
|
|
23
|
+
marginBottom: 6,
|
|
24
|
+
},
|
|
25
|
+
label: {
|
|
26
|
+
fontSize: 12,
|
|
27
|
+
fontWeight: '700',
|
|
28
|
+
},
|
|
29
|
+
value: {
|
|
30
|
+
fontSize: 12,
|
|
31
|
+
},
|
|
32
|
+
track: {
|
|
33
|
+
height: 8,
|
|
34
|
+
borderRadius: 999,
|
|
35
|
+
overflow: 'hidden',
|
|
36
|
+
},
|
|
37
|
+
fill: {
|
|
38
|
+
height: '100%',
|
|
39
|
+
borderRadius: 999,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Quest } from '../types';
|
|
2
|
+
interface QuestProgressProps {
|
|
3
|
+
quest: Quest;
|
|
4
|
+
primaryColor?: string;
|
|
5
|
+
backgroundColor?: string;
|
|
6
|
+
style?: object;
|
|
7
|
+
}
|
|
8
|
+
export declare function QuestProgress({ quest, primaryColor, backgroundColor, style, }: QuestProgressProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default QuestProgress;
|
|
10
|
+
//# sourceMappingURL=QuestProgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuestProgress.d.ts","sourceRoot":"","sources":["../../src/components/QuestProgress.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAMtC,UAAU,kBAAkB;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,YAAwB,EACxB,eAA2B,EAC3B,KAAK,GACN,EAAE,kBAAkB,2CA8CpB;AAiFD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Component
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
export function QuestProgress({ quest, primaryColor = '#6366F1', backgroundColor = '#E5E7EB', style, }) {
|
|
7
|
+
const progressPercent = quest.target > 0 ? (quest.progress / quest.target) * 100 : 0;
|
|
8
|
+
const isCompleted = quest.status === 'completed';
|
|
9
|
+
return (_jsxs(View, { style: [styles.container, style], children: [_jsxs(View, { style: styles.header, children: [_jsx(Text, { style: styles.name, children: quest.name }), isCompleted && _jsx(Text, { style: styles.completedBadge, children: "\u2713" })] }), _jsx(Text, { style: styles.description, children: quest.description }), _jsx(View, { style: [styles.progressBar, { backgroundColor }], children: _jsx(View, { style: [
|
|
10
|
+
styles.progressFill,
|
|
11
|
+
{
|
|
12
|
+
backgroundColor: isCompleted ? '#10B981' : primaryColor,
|
|
13
|
+
width: `${Math.min(progressPercent, 100)}%`,
|
|
14
|
+
},
|
|
15
|
+
] }) }), _jsxs(View, { style: styles.footer, children: [_jsxs(Text, { style: styles.progressText, children: [quest.progress, " / ", quest.target] }), _jsxs(Text, { style: styles.percentText, children: [Math.round(progressPercent), "%"] })] }), quest.rewards && quest.rewards.length > 0 && (_jsxs(View, { style: styles.rewards, children: [_jsx(Text, { style: styles.rewardLabel, children: "Rewards:" }), quest.rewards.map((reward, idx) => (_jsxs(Text, { style: styles.rewardText, children: [reward.type === 'points' && `${reward.amount} ${reward.currency}`, reward.type === 'badge' && `🏅 Badge`, reward.type === 'reward' && `🎁 Reward`] }, idx)))] }))] }));
|
|
16
|
+
}
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
// Styles
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
const styles = StyleSheet.create({
|
|
21
|
+
container: {
|
|
22
|
+
backgroundColor: '#FFFFFF',
|
|
23
|
+
borderRadius: 12,
|
|
24
|
+
padding: 16,
|
|
25
|
+
marginVertical: 8,
|
|
26
|
+
shadowColor: '#000',
|
|
27
|
+
shadowOffset: { width: 0, height: 2 },
|
|
28
|
+
shadowOpacity: 0.1,
|
|
29
|
+
shadowRadius: 4,
|
|
30
|
+
elevation: 3,
|
|
31
|
+
},
|
|
32
|
+
header: {
|
|
33
|
+
flexDirection: 'row',
|
|
34
|
+
justifyContent: 'space-between',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
marginBottom: 4,
|
|
37
|
+
},
|
|
38
|
+
name: {
|
|
39
|
+
fontSize: 16,
|
|
40
|
+
fontWeight: '600',
|
|
41
|
+
color: '#1F2937',
|
|
42
|
+
},
|
|
43
|
+
completedBadge: {
|
|
44
|
+
fontSize: 18,
|
|
45
|
+
color: '#10B981',
|
|
46
|
+
},
|
|
47
|
+
description: {
|
|
48
|
+
fontSize: 14,
|
|
49
|
+
color: '#6B7280',
|
|
50
|
+
marginBottom: 12,
|
|
51
|
+
},
|
|
52
|
+
progressBar: {
|
|
53
|
+
height: 8,
|
|
54
|
+
borderRadius: 4,
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
},
|
|
57
|
+
progressFill: {
|
|
58
|
+
height: '100%',
|
|
59
|
+
borderRadius: 4,
|
|
60
|
+
},
|
|
61
|
+
footer: {
|
|
62
|
+
flexDirection: 'row',
|
|
63
|
+
justifyContent: 'space-between',
|
|
64
|
+
marginTop: 8,
|
|
65
|
+
},
|
|
66
|
+
progressText: {
|
|
67
|
+
fontSize: 12,
|
|
68
|
+
color: '#6B7280',
|
|
69
|
+
},
|
|
70
|
+
percentText: {
|
|
71
|
+
fontSize: 12,
|
|
72
|
+
fontWeight: '600',
|
|
73
|
+
color: '#1F2937',
|
|
74
|
+
},
|
|
75
|
+
rewards: {
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
marginTop: 12,
|
|
79
|
+
paddingTop: 12,
|
|
80
|
+
borderTopWidth: 1,
|
|
81
|
+
borderTopColor: '#E5E7EB',
|
|
82
|
+
},
|
|
83
|
+
rewardLabel: {
|
|
84
|
+
fontSize: 12,
|
|
85
|
+
color: '#6B7280',
|
|
86
|
+
marginRight: 8,
|
|
87
|
+
},
|
|
88
|
+
rewardText: {
|
|
89
|
+
fontSize: 12,
|
|
90
|
+
color: '#1F2937',
|
|
91
|
+
marginRight: 8,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
export default QuestProgress;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadialGauge.d.ts","sourceRoot":"","sources":["../../src/components/RadialGauge.tsx"],"names":[],"mappings":"AAOA,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,gBAAgB,2CAgDlE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { View, StyleSheet, Text } from 'react-native';
|
|
3
|
+
import Svg, { Circle } from 'react-native-svg';
|
|
4
|
+
import { useTheme } from '../theme/context';
|
|
5
|
+
import { GradientCard } from './GradientCard';
|
|
6
|
+
export function RadialGauge({ value, max, label }) {
|
|
7
|
+
const theme = useTheme();
|
|
8
|
+
const size = 80;
|
|
9
|
+
const stroke = 6;
|
|
10
|
+
const radius = size / 2 - stroke;
|
|
11
|
+
const circumference = radius * 2 * Math.PI;
|
|
12
|
+
const strokeDashoffset = circumference - (value / max) * circumference;
|
|
13
|
+
const percentage = Math.round((value / max) * 100);
|
|
14
|
+
return (_jsx(GradientCard, { style: styles.container, children: _jsxs(View, { style: styles.content, children: [_jsxs(View, { style: styles.gaugeContainer, children: [_jsxs(Svg, { height: size, width: size, style: { transform: [{ rotate: '-90deg' }] }, children: [_jsx(Circle, { stroke: "rgba(255,255,255,0.1)", strokeWidth: stroke, fill: "transparent", r: radius, cx: size / 2, cy: size / 2 }), _jsx(Circle, { stroke: "#FFD700", strokeWidth: stroke, strokeDasharray: `${circumference} ${circumference}`, strokeDashoffset: strokeDashoffset, strokeLinecap: "round", fill: "transparent", r: radius, cx: size / 2, cy: size / 2 })] }), _jsx(View, { style: styles.centerText, children: _jsxs(Text, { style: styles.percentText, children: [percentage, "%"] }) })] }), _jsxs(View, { style: styles.infoContainer, children: [_jsx(Text, { style: [styles.label, { color: theme.colors.text.primary }], children: label }), _jsxs(Text, { style: [styles.subText, { color: theme.colors.text.secondary }], children: ["$", value, " of $", max, " saved."] })] })] }) }));
|
|
15
|
+
}
|
|
16
|
+
const styles = StyleSheet.create({
|
|
17
|
+
container: {
|
|
18
|
+
padding: 16,
|
|
19
|
+
marginBottom: 16,
|
|
20
|
+
},
|
|
21
|
+
content: {
|
|
22
|
+
flexDirection: 'row',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
},
|
|
25
|
+
gaugeContainer: {
|
|
26
|
+
position: 'relative',
|
|
27
|
+
width: 80,
|
|
28
|
+
height: 80,
|
|
29
|
+
},
|
|
30
|
+
centerText: {
|
|
31
|
+
...StyleSheet.absoluteFillObject,
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
justifyContent: 'center',
|
|
34
|
+
},
|
|
35
|
+
percentText: {
|
|
36
|
+
fontSize: 16,
|
|
37
|
+
fontWeight: '800',
|
|
38
|
+
color: 'white',
|
|
39
|
+
},
|
|
40
|
+
infoContainer: {
|
|
41
|
+
flex: 1,
|
|
42
|
+
marginLeft: 16,
|
|
43
|
+
},
|
|
44
|
+
label: {
|
|
45
|
+
fontSize: 16,
|
|
46
|
+
fontWeight: '700',
|
|
47
|
+
marginBottom: 2,
|
|
48
|
+
},
|
|
49
|
+
subText: {
|
|
50
|
+
fontSize: 12,
|
|
51
|
+
lineHeight: 16,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Reward } from '../types';
|
|
2
|
+
interface RewardCardProps {
|
|
3
|
+
reward: Reward;
|
|
4
|
+
onRedeem?: () => void;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
style?: object;
|
|
7
|
+
}
|
|
8
|
+
export declare function RewardCard({ reward, onRedeem, disabled, style }: RewardCardProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default RewardCard;
|
|
10
|
+
//# sourceMappingURL=RewardCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RewardCard.d.ts","sourceRoot":"","sources":["../../src/components/RewardCard.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMvC,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAgB,EAAE,KAAK,EAAE,EAAE,eAAe,2CA6CxF;AAsDD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { View, Text, Image, StyleSheet } from 'react-native';
|
|
3
|
+
import { GradientCard } from './GradientCard';
|
|
4
|
+
import { Button } from './Button';
|
|
5
|
+
import { useTheme } from '../theme/context';
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// Component
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
export function RewardCard({ reward, onRedeem, disabled = false, style }) {
|
|
10
|
+
const theme = useTheme();
|
|
11
|
+
const isAvailable = reward.available && !disabled;
|
|
12
|
+
return (_jsxs(GradientCard, { style: [styles.container, style], children: [reward.imageUrl ? (_jsx(Image, { source: { uri: reward.imageUrl }, style: styles.image, resizeMode: "cover" })) : (_jsx(View, { style: [styles.imagePlaceholder, { backgroundColor: 'rgba(255, 69, 0, 0.1)' }], children: _jsx(Text, { style: styles.placeholderText, children: "\uD83C\uDF81" }) })), _jsxs(View, { style: styles.content, children: [_jsx(Text, { style: [styles.name, { color: theme.colors.text.primary }], children: reward.name }), _jsx(Text, { style: [styles.description, { color: theme.colors.text.secondary }], numberOfLines: 2, children: reward.description }), _jsxs(View, { style: styles.footer, children: [_jsxs(View, { style: styles.cost, children: [_jsx(Text, { style: [styles.costAmount, { color: theme.colors.text.primary }], children: reward.cost }), _jsx(Text, { style: [styles.costCurrency, { color: theme.colors.text.secondary }], children: reward.currency })] }), onRedeem && (_jsx(Button, { title: isAvailable ? 'Redeem' : 'Unavailable', onPress: onRedeem, variant: isAvailable ? 'primary' : 'secondary', style: { opacity: isAvailable ? 1 : 0.6 } }))] })] })] }));
|
|
13
|
+
}
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
// Styles
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
const styles = StyleSheet.create({
|
|
18
|
+
container: {
|
|
19
|
+
marginVertical: 8,
|
|
20
|
+
},
|
|
21
|
+
image: {
|
|
22
|
+
width: '100%',
|
|
23
|
+
height: 140,
|
|
24
|
+
},
|
|
25
|
+
imagePlaceholder: {
|
|
26
|
+
width: '100%',
|
|
27
|
+
height: 140,
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
},
|
|
31
|
+
placeholderText: {
|
|
32
|
+
fontSize: 48,
|
|
33
|
+
},
|
|
34
|
+
content: {
|
|
35
|
+
padding: 16,
|
|
36
|
+
},
|
|
37
|
+
name: {
|
|
38
|
+
fontSize: 18,
|
|
39
|
+
fontWeight: '700',
|
|
40
|
+
marginBottom: 4,
|
|
41
|
+
},
|
|
42
|
+
description: {
|
|
43
|
+
fontSize: 14,
|
|
44
|
+
marginBottom: 16,
|
|
45
|
+
},
|
|
46
|
+
footer: {
|
|
47
|
+
flexDirection: 'row',
|
|
48
|
+
justifyContent: 'space-between',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
},
|
|
51
|
+
cost: {
|
|
52
|
+
flexDirection: 'row',
|
|
53
|
+
alignItems: 'baseline',
|
|
54
|
+
},
|
|
55
|
+
costAmount: {
|
|
56
|
+
fontSize: 20,
|
|
57
|
+
fontWeight: '800',
|
|
58
|
+
},
|
|
59
|
+
costCurrency: {
|
|
60
|
+
fontSize: 12,
|
|
61
|
+
marginLeft: 4,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
export default RewardCard;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RulesModal.d.ts","sourceRoot":"","sources":["../../src/components/RulesModal.tsx"],"names":[],"mappings":"AAcA,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,eAAe,2CAwE/D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Modal, View, Text, StyleSheet, ScrollView, TouchableOpacity, SafeAreaView, } from 'react-native';
|
|
3
|
+
import { useTheme } from '../theme/context';
|
|
4
|
+
import { GradientCard } from './GradientCard';
|
|
5
|
+
export function RulesModal({ visible, onClose }) {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
const rules = [
|
|
8
|
+
{ icon: '🛍️', title: 'Spend & Earn', desc: 'Earn 1 XP for every $5 spent on your QwikCard.' },
|
|
9
|
+
{ icon: '🧾', title: 'Pay Bills', desc: 'Get +50 XP and 1 Qwik Coin for every bill paid.' },
|
|
10
|
+
{
|
|
11
|
+
icon: '🔥',
|
|
12
|
+
title: 'Streaks',
|
|
13
|
+
desc: 'Maintain a daily login streak for massive XP milestones.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
icon: '🤝',
|
|
17
|
+
title: 'Refer Friends',
|
|
18
|
+
desc: 'Earn +500 XP and 5 Qwik Coins when friends activate.',
|
|
19
|
+
},
|
|
20
|
+
{ icon: '📊', title: 'Credit Checks', desc: 'Check your credit score weekly to earn +50 XP.' },
|
|
21
|
+
{
|
|
22
|
+
icon: '💡',
|
|
23
|
+
title: 'Financial Literacy',
|
|
24
|
+
desc: 'Complete quizzes to earn +100 XP per lesson.',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
return (_jsx(Modal, { visible: visible, animationType: "slide", transparent: true, onRequestClose: onClose, children: _jsx(View, { style: styles.overlay, children: _jsx(SafeAreaView, { style: styles.safeArea, children: _jsxs(View, { style: [styles.container, { backgroundColor: theme.colors.background }], children: [_jsxs(View, { style: styles.header, children: [_jsx(Text, { style: [styles.title, { color: theme.colors.text.primary }], children: "How to Play" }), _jsx(TouchableOpacity, { onPress: onClose, style: styles.closeButton, children: _jsx(Text, { style: { fontSize: 24, color: theme.colors.text.secondary }, children: "\u2715" }) })] }), _jsxs(ScrollView, { contentContainerStyle: styles.scrollContent, children: [_jsx(Text, { style: [styles.subtitle, { color: theme.colors.text.secondary }], children: "Level up your financial future by building healthy habits. Here is how you earn rewards:" }), rules.map((rule, index) => (_jsx(GradientCard, { style: styles.ruleCard, children: _jsxs(View, { style: styles.ruleRow, children: [_jsx(View, { style: styles.iconContainer, children: _jsx(Text, { style: { fontSize: 24 }, children: rule.icon }) }), _jsxs(View, { style: styles.textContainer, children: [_jsx(Text, { style: [styles.ruleTitle, { color: theme.colors.text.primary }], children: rule.title }), _jsx(Text, { style: [styles.ruleDesc, { color: theme.colors.text.secondary }], children: rule.desc })] })] }) }, index))), _jsx(TouchableOpacity, { onPress: onClose, style: [styles.actionButton, { backgroundColor: theme.colors.primary }], children: _jsx(Text, { style: styles.actionButtonText, children: "Got it!" }) })] })] }) }) }) }));
|
|
28
|
+
}
|
|
29
|
+
const styles = StyleSheet.create({
|
|
30
|
+
overlay: {
|
|
31
|
+
flex: 1,
|
|
32
|
+
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
|
33
|
+
},
|
|
34
|
+
safeArea: {
|
|
35
|
+
flex: 1,
|
|
36
|
+
justifyContent: 'flex-end',
|
|
37
|
+
},
|
|
38
|
+
container: {
|
|
39
|
+
borderTopLeftRadius: 32,
|
|
40
|
+
borderTopRightRadius: 32,
|
|
41
|
+
height: '90%',
|
|
42
|
+
padding: 24,
|
|
43
|
+
},
|
|
44
|
+
header: {
|
|
45
|
+
flexDirection: 'row',
|
|
46
|
+
justifyContent: 'space-between',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
marginBottom: 20,
|
|
49
|
+
},
|
|
50
|
+
title: {
|
|
51
|
+
fontSize: 24,
|
|
52
|
+
fontWeight: '900',
|
|
53
|
+
},
|
|
54
|
+
closeButton: {
|
|
55
|
+
padding: 8,
|
|
56
|
+
},
|
|
57
|
+
scrollContent: {
|
|
58
|
+
paddingBottom: 40,
|
|
59
|
+
},
|
|
60
|
+
subtitle: {
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
lineHeight: 20,
|
|
63
|
+
marginBottom: 24,
|
|
64
|
+
},
|
|
65
|
+
ruleCard: {
|
|
66
|
+
padding: 16,
|
|
67
|
+
marginBottom: 12,
|
|
68
|
+
borderRadius: 20,
|
|
69
|
+
},
|
|
70
|
+
ruleRow: {
|
|
71
|
+
flexDirection: 'row',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
},
|
|
74
|
+
iconContainer: {
|
|
75
|
+
width: 48,
|
|
76
|
+
height: 48,
|
|
77
|
+
borderRadius: 14,
|
|
78
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
justifyContent: 'center',
|
|
81
|
+
marginRight: 16,
|
|
82
|
+
},
|
|
83
|
+
textContainer: {
|
|
84
|
+
flex: 1,
|
|
85
|
+
},
|
|
86
|
+
ruleTitle: {
|
|
87
|
+
fontSize: 16,
|
|
88
|
+
fontWeight: '700',
|
|
89
|
+
marginBottom: 2,
|
|
90
|
+
},
|
|
91
|
+
ruleDesc: {
|
|
92
|
+
fontSize: 12,
|
|
93
|
+
lineHeight: 16,
|
|
94
|
+
},
|
|
95
|
+
actionButton: {
|
|
96
|
+
marginTop: 24,
|
|
97
|
+
padding: 18,
|
|
98
|
+
borderRadius: 18,
|
|
99
|
+
alignItems: 'center',
|
|
100
|
+
},
|
|
101
|
+
actionButtonText: {
|
|
102
|
+
color: 'white',
|
|
103
|
+
fontSize: 16,
|
|
104
|
+
fontWeight: '800',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Badge } from '../types';
|
|
2
|
+
interface UseBadgesReturn {
|
|
3
|
+
badges: Badge[];
|
|
4
|
+
earnedBadges: Badge[];
|
|
5
|
+
allBadges: Badge[];
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
refresh: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function useBadges(): UseBadgesReturn;
|
|
11
|
+
export default useBadges;
|
|
12
|
+
//# sourceMappingURL=useBadges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBadges.d.ts","sourceRoot":"","sources":["../../src/hooks/useBadges.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,UAAU,eAAe;IACvB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY,EAAE,KAAK,EAAE,CAAC;IACtB,SAAS,EAAE,KAAK,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,wBAAgB,SAAS,IAAI,eAAe,CA0C3C;AAED,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { usePlaybasis } from '../PlaybasisProvider';
|
|
3
|
+
export function useBadges() {
|
|
4
|
+
const { client, playerId } = usePlaybasis();
|
|
5
|
+
const [badges, setBadges] = useState([]);
|
|
6
|
+
const [allBadges, setAllBadges] = useState([]);
|
|
7
|
+
const [loading, setLoading] = useState(true);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const fetchBadges = useCallback(async () => {
|
|
10
|
+
setLoading(true);
|
|
11
|
+
setError(null);
|
|
12
|
+
try {
|
|
13
|
+
// Fetch all available badges
|
|
14
|
+
const all = await client.getBadges();
|
|
15
|
+
setAllBadges(all);
|
|
16
|
+
// Fetch player's earned badges
|
|
17
|
+
if (playerId) {
|
|
18
|
+
const playerBadges = await client.getPlayerBadges(playerId);
|
|
19
|
+
setBadges(playerBadges);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
setError(err instanceof Error ? err : new Error('Failed to fetch badges'));
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setLoading(false);
|
|
27
|
+
}
|
|
28
|
+
}, [client, playerId]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
fetchBadges();
|
|
31
|
+
}, [fetchBadges]);
|
|
32
|
+
const earnedBadges = badges.filter((b) => b.isEarned);
|
|
33
|
+
return {
|
|
34
|
+
badges,
|
|
35
|
+
earnedBadges,
|
|
36
|
+
allBadges,
|
|
37
|
+
loading,
|
|
38
|
+
error,
|
|
39
|
+
refresh: fetchBadges,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export default useBadges;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PointBalance, Currency } from '../types';
|
|
2
|
+
interface UsePointsReturn {
|
|
3
|
+
balances: PointBalance[];
|
|
4
|
+
xp: number;
|
|
5
|
+
qwikCoins: number;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
refresh: () => Promise<void>;
|
|
9
|
+
earn: (currency: Currency, amount: number, reason?: string) => Promise<PointBalance | null>;
|
|
10
|
+
}
|
|
11
|
+
export declare function usePoints(): UsePointsReturn;
|
|
12
|
+
export default usePoints;
|
|
13
|
+
//# sourceMappingURL=usePoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePoints.d.ts","sourceRoot":"","sources":["../../src/hooks/usePoints.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEvD,UAAU,eAAe;IACvB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAC7F;AAED,wBAAgB,SAAS,IAAI,eAAe,CAyE3C;AAED,eAAe,SAAS,CAAC"}
|