Skip to content

Commit 5f49a64

Browse files
committed
Refactor App component and extract functionalities into reusable components
- Simplified App component by extracting header, payment event, loading indicator, payment buttons, and transaction info into separate components. - Introduced a theme for consistent styling across the application. - Created a Button component for reusable button styles and states. - Added a Card component for consistent card styling. - Implemented a LoadingIndicator component for better loading state management. - Created PaymentButtons component to handle payment actions. - Added PaymentEvent component to display payment event messages. - Introduced TransactionInfo component to show details of the last transaction. - Refactored payment operations into a custom hook for better state management and separation of concerns. - Added utility functions for alert handling and async operations. - Defined constants for payment amounts and terminal activation code.
1 parent adfbf43 commit 5f49a64

17 files changed

Lines changed: 916 additions & 401 deletions

example/src/App.tsx

Lines changed: 55 additions & 401 deletions
Large diffs are not rendered by default.

example/src/components/Button.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React from 'react';
2+
import {
3+
TouchableOpacity,
4+
Text,
5+
StyleSheet,
6+
type TouchableOpacityProps,
7+
} from 'react-native';
8+
import { theme } from '../constants/theme';
9+
10+
export type ButtonVariant =
11+
| 'primary'
12+
| 'secondary'
13+
| 'success'
14+
| 'warning'
15+
| 'error';
16+
17+
interface ButtonProps extends TouchableOpacityProps {
18+
title: string;
19+
variant?: ButtonVariant;
20+
fullWidth?: boolean;
21+
loading?: boolean;
22+
}
23+
24+
export const Button: React.FC<ButtonProps> = ({
25+
title,
26+
variant = 'primary',
27+
fullWidth = true,
28+
loading = false,
29+
disabled,
30+
style,
31+
...props
32+
}) => {
33+
const buttonStyle = [
34+
styles.button,
35+
styles[variant],
36+
fullWidth && styles.fullWidth,
37+
(disabled || loading) && styles.disabled,
38+
style,
39+
];
40+
41+
const textStyle = [styles.text, (disabled || loading) && styles.disabledText];
42+
43+
return (
44+
<TouchableOpacity
45+
style={buttonStyle}
46+
disabled={disabled || loading}
47+
activeOpacity={0.8}
48+
{...props}
49+
>
50+
<Text style={textStyle}>{loading ? 'Processando...' : title}</Text>
51+
</TouchableOpacity>
52+
);
53+
};
54+
55+
const styles = StyleSheet.create({
56+
button: {
57+
paddingVertical: theme.spacing.md,
58+
paddingHorizontal: theme.spacing.lg,
59+
borderRadius: theme.borderRadius.md,
60+
alignItems: 'center',
61+
justifyContent: 'center',
62+
marginBottom: theme.spacing.sm,
63+
...theme.shadows.small,
64+
},
65+
fullWidth: {
66+
width: '100%',
67+
},
68+
text: {
69+
...theme.typography.body,
70+
fontWeight: '600',
71+
color: theme.colors.textPrimary,
72+
},
73+
disabled: {
74+
opacity: 0.6,
75+
},
76+
disabledText: {
77+
color: theme.colors.textSecondary,
78+
},
79+
80+
// Variants
81+
primary: {
82+
backgroundColor: theme.colors.primary,
83+
},
84+
secondary: {
85+
backgroundColor: theme.colors.secondary,
86+
},
87+
success: {
88+
backgroundColor: theme.colors.success,
89+
},
90+
warning: {
91+
backgroundColor: theme.colors.warning,
92+
},
93+
error: {
94+
backgroundColor: theme.colors.error,
95+
},
96+
});

example/src/components/Card.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { View, StyleSheet, type ViewProps } from 'react-native';
3+
import { theme } from '../constants/theme';
4+
5+
interface CardProps extends ViewProps {
6+
elevated?: boolean;
7+
children: React.ReactNode;
8+
}
9+
10+
export const Card: React.FC<CardProps> = ({
11+
elevated = false,
12+
children,
13+
style,
14+
...props
15+
}) => {
16+
const cardStyle = [styles.card, elevated && styles.elevated, style];
17+
18+
return (
19+
<View style={cardStyle} {...props}>
20+
{children}
21+
</View>
22+
);
23+
};
24+
25+
const styles = StyleSheet.create({
26+
card: {
27+
backgroundColor: theme.colors.surface,
28+
borderRadius: theme.borderRadius.md,
29+
padding: theme.spacing.md,
30+
marginBottom: theme.spacing.sm,
31+
},
32+
elevated: {
33+
backgroundColor: theme.colors.surfaceElevated,
34+
...theme.shadows.medium,
35+
},
36+
});

example/src/components/Header.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import { View, Text, StyleSheet } from 'react-native';
3+
import { theme } from '../constants/theme';
4+
import { getStatusColor, getStatusText } from '../utils/payment';
5+
import { Card } from './Card';
6+
7+
interface HeaderProps {
8+
terminalSerial: string;
9+
isInitialized: boolean;
10+
}
11+
12+
export const Header: React.FC<HeaderProps> = ({
13+
terminalSerial,
14+
isInitialized,
15+
}) => {
16+
const statusColor = getStatusColor(isInitialized);
17+
const statusText = getStatusText(isInitialized);
18+
19+
return (
20+
<Card elevated>
21+
<View style={styles.container}>
22+
<Text style={styles.title}>Demo PlugPag Nitro</Text>
23+
<Text style={styles.subtitle}>Terminal: {terminalSerial}</Text>
24+
<Text style={[styles.status, { color: statusColor }]}>
25+
Status: {statusText}
26+
</Text>
27+
</View>
28+
</Card>
29+
);
30+
};
31+
32+
const styles = StyleSheet.create({
33+
container: {
34+
alignItems: 'center',
35+
},
36+
title: {
37+
...theme.typography.h1,
38+
color: theme.colors.textPrimary,
39+
marginBottom: theme.spacing.xs,
40+
},
41+
subtitle: {
42+
...theme.typography.body,
43+
color: theme.colors.textSecondary,
44+
marginBottom: theme.spacing.xs,
45+
},
46+
status: {
47+
...theme.typography.caption,
48+
fontWeight: '600',
49+
},
50+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import { Text, ActivityIndicator, StyleSheet } from 'react-native';
3+
import { theme } from '../constants/theme';
4+
import { Card } from './Card';
5+
6+
interface LoadingIndicatorProps {
7+
visible: boolean;
8+
message?: string;
9+
}
10+
11+
export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
12+
visible,
13+
message = 'Processando...',
14+
}) => {
15+
if (!visible) return null;
16+
17+
return (
18+
<Card elevated style={styles.container}>
19+
<ActivityIndicator size="large" color={theme.colors.primary} />
20+
<Text style={styles.text}>{message}</Text>
21+
</Card>
22+
);
23+
};
24+
25+
const styles = StyleSheet.create({
26+
container: {
27+
alignItems: 'center',
28+
paddingVertical: theme.spacing.lg,
29+
},
30+
text: {
31+
...theme.typography.body,
32+
color: theme.colors.textSecondary,
33+
marginTop: theme.spacing.sm,
34+
},
35+
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React from 'react';
2+
import { View, StyleSheet } from 'react-native';
3+
import { Button } from './Button';
4+
import { PAYMENT_BUTTONS } from '../utils/payment';
5+
import { InstallmentType } from 'react-native-plugpag-nitro';
6+
7+
interface PaymentButtonsProps {
8+
isInitialized: boolean;
9+
isProcessing: boolean;
10+
hasLastPayment: boolean;
11+
onInitialize: () => void;
12+
onPayment: (options: {
13+
amount: number;
14+
type: any;
15+
installmentType?: InstallmentType;
16+
installments?: number;
17+
}) => void;
18+
onRefund: () => void;
19+
}
20+
21+
export const PaymentButtons: React.FC<PaymentButtonsProps> = ({
22+
isInitialized,
23+
isProcessing,
24+
hasLastPayment,
25+
onInitialize,
26+
onPayment,
27+
onRefund,
28+
}) => {
29+
const handlePaymentClick = (
30+
buttonConfig: (typeof PAYMENT_BUTTONS)[number]
31+
) => {
32+
const paymentOptions = {
33+
amount: buttonConfig.amount,
34+
type: buttonConfig.paymentType,
35+
...('installments' in buttonConfig && {
36+
installmentType: InstallmentType.BUYER_INSTALLMENT,
37+
installments: buttonConfig.installments,
38+
}),
39+
};
40+
onPayment(paymentOptions);
41+
};
42+
43+
return (
44+
<View style={styles.container}>
45+
<Button
46+
title="Inicializar Terminal"
47+
variant={!isInitialized ? 'success' : 'primary'}
48+
onPress={onInitialize}
49+
disabled={isProcessing}
50+
loading={isProcessing}
51+
/>
52+
53+
{PAYMENT_BUTTONS.map((buttonConfig) => (
54+
<Button
55+
key={buttonConfig.id}
56+
title={buttonConfig.title}
57+
variant={buttonConfig.variant}
58+
onPress={() => handlePaymentClick(buttonConfig)}
59+
disabled={isProcessing || !isInitialized}
60+
/>
61+
))}
62+
63+
<Button
64+
title="Estornar Última Transação"
65+
variant="warning"
66+
onPress={onRefund}
67+
disabled={isProcessing || !hasLastPayment}
68+
/>
69+
</View>
70+
);
71+
};
72+
73+
const styles = StyleSheet.create({
74+
container: {
75+
flex: 1,
76+
},
77+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from 'react';
2+
import { Text, StyleSheet } from 'react-native';
3+
import { theme } from '../constants/theme';
4+
import { getEventColor } from '../utils/payment';
5+
import { Card } from './Card';
6+
7+
interface PaymentEventProps {
8+
code: number;
9+
message: string;
10+
customMessage?: string;
11+
}
12+
13+
export const PaymentEvent: React.FC<PaymentEventProps> = ({
14+
code,
15+
message,
16+
customMessage,
17+
}) => {
18+
if (code <= 0) return null;
19+
20+
const eventColor = getEventColor(code);
21+
22+
return (
23+
<Card style={[styles.container, { backgroundColor: eventColor }]}>
24+
<Text style={styles.message}>{message}</Text>
25+
<Text style={styles.code}>Código: {code}</Text>
26+
{customMessage && (
27+
<Text style={styles.customMessage}>{customMessage}</Text>
28+
)}
29+
</Card>
30+
);
31+
};
32+
33+
const styles = StyleSheet.create({
34+
container: {
35+
backgroundColor: theme.colors.primary,
36+
},
37+
message: {
38+
...theme.typography.body,
39+
color: theme.colors.textPrimary,
40+
fontWeight: '600',
41+
marginBottom: theme.spacing.xs,
42+
},
43+
code: {
44+
...theme.typography.small,
45+
color: theme.colors.textPrimary,
46+
opacity: 0.9,
47+
},
48+
customMessage: {
49+
...theme.typography.caption,
50+
color: theme.colors.textPrimary,
51+
fontStyle: 'italic',
52+
marginTop: theme.spacing.xs,
53+
},
54+
});

0 commit comments

Comments
 (0)