@xetwa/design-system 1.0.39 → 1.0.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xetwa/design-system",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { WordChipIcon } from './WordChipIcon';
3
+ import { Calendar, Bell } from 'lucide-react';
4
+ import React from 'react';
5
+
6
+ const meta = {
7
+ title: 'Components/Buttons/WordChipIcon',
8
+ component: WordChipIcon,
9
+ parameters: {
10
+ layout: 'centered',
11
+ docs: {
12
+ description: {
13
+ component: 'O **WordChipIcon** é um WordChip que permite renderizar um ícone no lado esquerdo ou direito.',
14
+ },
15
+ },
16
+ },
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ word: { control: 'text' },
20
+ iconPosition: {
21
+ control: 'radio',
22
+ options: ['left', 'right'],
23
+ },
24
+ state: {
25
+ control: 'select',
26
+ options: ['default', 'selected', 'correct', 'wrong', 'used'],
27
+ },
28
+ disabled: { control: 'boolean' },
29
+ fullWidth: { control: 'boolean' },
30
+ size: {
31
+ control: 'select',
32
+ options: ['sm', 'md', 'lg'],
33
+ },
34
+ },
35
+ } satisfies Meta<typeof WordChipIcon>;
36
+
37
+ export default meta;
38
+ type Story = StoryObj<typeof meta>;
39
+
40
+ export const Default: Story = {
41
+ args: {
42
+ word: 'Calendário',
43
+ icon: <Calendar size={20} color="#cf7a30" />,
44
+ iconPosition: 'left',
45
+ state: 'default',
46
+ },
47
+ };
48
+
49
+ export const RightIcon: Story = {
50
+ args: {
51
+ word: 'Lembrete',
52
+ icon: <Bell size={20} color="#cf7a30" />,
53
+ iconPosition: 'right',
54
+ state: 'default',
55
+ },
56
+ };
57
+
58
+ export const AllStates: Story = {
59
+ render: () => (
60
+ <div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap', maxWidth: '600px' }}>
61
+ <WordChipIcon word="Calendário" icon={<Calendar size={20} color="#cf7a30" />} state="default" />
62
+ <WordChipIcon word="Lembrete" icon={<Bell size={20} color="#cf7a30" />} state="selected" />
63
+ <WordChipIcon word="Alarme" icon={<Bell size={20} color="#cf7a30" />} state="correct" />
64
+ <WordChipIcon word="Erro" icon={<Calendar size={20} color="#cf7a30" />} state="wrong" />
65
+ <WordChipIcon word="Usado" icon={<Calendar size={20} color="#cf7a30" />} state="used" />
66
+ </div>
67
+ ),
68
+ };
@@ -0,0 +1,186 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { View, StyleSheet, Pressable, Animated } from 'react-native';
3
+ import type { ViewStyle } from 'react-native';
4
+ import { theme } from '../../../styles/theme';
5
+ import { useResponsive } from '../../../hooks/useResponsive';
6
+ import { Typography } from '../../Typography/Typography';
7
+ import type { WordChipState } from '../WordChip/WordChip';
8
+
9
+ export interface WordChipIconProps {
10
+ /** A palavra ou texto a apresentar. */
11
+ word: string;
12
+ /** O ícone a ser renderizado. */
13
+ icon: React.ReactNode;
14
+ /** A posição do ícone. Padrão: 'left' */
15
+ iconPosition?: 'left' | 'right';
16
+ /** Estado atual do chip. */
17
+ state?: WordChipState;
18
+ /** Função disparada ao clicar. */
19
+ onPress?: () => void;
20
+ /** Estilos adicionais para o container. */
21
+ style?: ViewStyle | ViewStyle[];
22
+ /** Impede cliques. */
23
+ disabled?: boolean;
24
+ /** Se verdadeiro, ocupa a largura total disponível. */
25
+ fullWidth?: boolean;
26
+ /** Tamanho do chip. */
27
+ size?: 'sm' | 'md' | 'lg';
28
+ /** Largura personalizada. Ignora max-width e align-self flex-start. */
29
+ width?: number | string;
30
+ }
31
+
32
+ export const WordChipIcon = ({
33
+ word,
34
+ icon,
35
+ iconPosition = 'left',
36
+ state = 'default',
37
+ onPress,
38
+ style,
39
+ disabled = false,
40
+ fullWidth = false,
41
+ size = 'md',
42
+ width,
43
+ }: WordChipIconProps) => {
44
+ const { scale } = useResponsive();
45
+ const [isPressed, setIsPressed] = useState(false);
46
+ const scaleAnim = useRef(new Animated.Value(state === 'selected' ? 0.96 : 1)).current;
47
+
48
+ // Animação ao entrar na resposta (bounce) quando o estado passa a 'selected'
49
+ useEffect(() => {
50
+ if (state === 'selected') {
51
+ scaleAnim.setValue(0.96);
52
+ Animated.spring(scaleAnim, {
53
+ toValue: 1,
54
+ friction: 4,
55
+ tension: 100,
56
+ useNativeDriver: true,
57
+ }).start();
58
+ } else {
59
+ scaleAnim.setValue(1);
60
+ }
61
+ }, [state, scaleAnim]);
62
+
63
+ const getStateStyles = () => {
64
+ switch (state) {
65
+ case 'selected':
66
+ return {
67
+ bg: theme.colors.primaryLight,
68
+ border: theme.colors.primary,
69
+ shadow: 'rgba(242, 151, 75, 0.25)',
70
+ text: theme.colors.brown[800],
71
+ };
72
+ case 'correct':
73
+ return {
74
+ bg: '#eafaf2',
75
+ border: theme.colors.success,
76
+ shadow: 'rgba(22, 145, 90, 0.2)',
77
+ text: theme.colors.success,
78
+ };
79
+ case 'wrong':
80
+ return {
81
+ bg: '#fdf0ee',
82
+ border: theme.colors.error,
83
+ shadow: 'rgba(207, 91, 72, 0.2)',
84
+ text: theme.colors.error,
85
+ };
86
+ case 'used':
87
+ return {
88
+ bg: theme.colors.white,
89
+ border: theme.colors.brown[100],
90
+ shadow: theme.colors.brown[100],
91
+ text: theme.colors.brown[300],
92
+ };
93
+ case 'default':
94
+ default:
95
+ return {
96
+ bg: theme.colors.white,
97
+ border: theme.colors.brown[100],
98
+ shadow: theme.colors.brown[100],
99
+ text: theme.colors.brown[800],
100
+ };
101
+ }
102
+ };
103
+
104
+ const currentStyles = getStateStyles();
105
+ const isDisabled = disabled || state === 'used';
106
+
107
+ // Press state visual adjustments
108
+ const showPressed = isPressed && !isDisabled;
109
+
110
+ const getTypographyVariant = () => {
111
+ switch (size) {
112
+ case 'lg': return 'btnLg';
113
+ case 'sm': return 'bodyMd';
114
+ case 'md':
115
+ default: return 'bodyLg';
116
+ }
117
+ };
118
+
119
+ return (
120
+ <Animated.View style={[{ transform: [{ scale: scaleAnim }] }, width ? { width } : null, style]}>
121
+ <Pressable
122
+ onPressIn={() => setIsPressed(true)}
123
+ onPressOut={() => setIsPressed(false)}
124
+ onPress={onPress}
125
+ disabled={isDisabled}
126
+ style={{ alignSelf: width ? 'stretch' : (fullWidth ? 'stretch' : 'flex-start') }}
127
+ >
128
+ <View style={[
129
+ styles.shadowContainer,
130
+ {
131
+ backgroundColor: currentStyles.shadow,
132
+ borderRadius: scale(12),
133
+ paddingBottom: showPressed ? 0 : scale(3),
134
+ marginTop: showPressed ? scale(3) : 0,
135
+ },
136
+ (fullWidth || width) ? { alignSelf: 'stretch', width: '100%' } : null
137
+ ]}>
138
+ <View style={[
139
+ styles.innerContainer,
140
+ {
141
+ backgroundColor: currentStyles.bg,
142
+ borderColor: currentStyles.border,
143
+ borderRadius: scale(12),
144
+ paddingVertical: scale(size === 'lg' ? 14 : size === 'sm' ? 6 : 9),
145
+ paddingHorizontal: scale(size === 'lg' ? 20 : size === 'sm' ? 10 : 14),
146
+ minHeight: scale(size === 'lg' ? 56 : size === 'sm' ? 36 : 44),
147
+ },
148
+ (fullWidth || width) ? { width: '100%' } : null
149
+ ]}>
150
+ <View style={[
151
+ styles.contentRow,
152
+ { flexDirection: iconPosition === 'right' ? 'row-reverse' : 'row' }
153
+ ]}>
154
+ {icon}
155
+ <Typography variant={getTypographyVariant()} style={[
156
+ styles.text,
157
+ { color: currentStyles.text }
158
+ ]}>
159
+ {word}
160
+ </Typography>
161
+ </View>
162
+ </View>
163
+ </View>
164
+ </Pressable>
165
+ </Animated.View>
166
+ );
167
+ };
168
+
169
+ const styles = StyleSheet.create({
170
+ shadowContainer: {
171
+ alignSelf: 'flex-start',
172
+ },
173
+ innerContainer: {
174
+ borderWidth: 2,
175
+ alignItems: 'center',
176
+ justifyContent: 'center',
177
+ },
178
+ contentRow: {
179
+ alignItems: 'center',
180
+ justifyContent: 'center',
181
+ gap: 8,
182
+ },
183
+ text: {
184
+ textAlign: 'center',
185
+ },
186
+ });
@@ -0,0 +1 @@
1
+ export * from './WordChipIcon';
@@ -1,9 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
- import { View, Pressable } from 'react-native';
3
- import type { ViewStyle, DimensionValue } from 'react-native';
2
+ import type { DimensionValue, ViewStyle } from 'react-native';
3
+ import { Pressable, View } from 'react-native';
4
4
  import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
5
- import { theme } from '../../../styles/theme';
6
5
  import { useResponsive } from '../../../hooks/useResponsive';
6
+ import { theme } from '../../../styles/theme';
7
7
 
8
8
  const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
9
9
 
@@ -15,41 +15,41 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
15
15
  export interface BaseCardProps {
16
16
  /** O conteúdo a ser renderizado dentro do cartão. */
17
17
  children?: ReactNode;
18
-
18
+
19
19
  /** Variante de estilo do cartão */
20
20
  variant?: 'default' | 'soft';
21
-
21
+
22
22
  // Container styling
23
23
  backgroundColor?: string;
24
24
  borderRadius?: number;
25
25
  borderWidth?: number;
26
26
  borderColor?: string;
27
27
  borderStyle?: 'solid' | 'dashed';
28
-
28
+
29
29
  // Padding & Layout
30
30
  padding?: number;
31
31
  paddingVertical?: number;
32
32
  paddingHorizontal?: number;
33
-
33
+
34
34
  // Sizing
35
35
  width?: DimensionValue;
36
36
  height?: DimensionValue;
37
37
  minHeight?: DimensionValue;
38
-
38
+
39
39
  // Shadow presets: 'none' | 'sm' | 'md' | 'lg'
40
40
  shadow?: 'none' | 'sm' | 'md' | 'lg';
41
41
  shadowColor?: string;
42
-
42
+
43
43
  // Interactive / Pressable behavior
44
44
  onPress?: () => void;
45
45
  disabled?: boolean;
46
-
46
+
47
47
  // Selection state
48
48
  isSelected?: boolean;
49
49
  selectedBorderColor?: string;
50
50
  selectedBackgroundColor?: string;
51
51
  selectedShadowColor?: string;
52
-
52
+
53
53
  // Opacity & other styles
54
54
  opacity?: number;
55
55
  overflow?: 'visible' | 'hidden';
@@ -107,12 +107,12 @@ export const BaseCard = ({
107
107
  const activeBorderWidth = isSelected ? 2 : borderWidth;
108
108
  const activeShadow = isSelected
109
109
  ? {
110
- shadowColor: selectedShadowColor || '#f2974b',
111
- shadowOffset: { width: 0, height: scale(4) },
112
- shadowOpacity: 0.14,
113
- shadowRadius: scale(14),
114
- elevation: 4,
115
- }
110
+ shadowColor: selectedShadowColor || '#f2974b',
111
+ shadowOffset: { width: 0, height: scale(4) },
112
+ shadowOpacity: 0.14,
113
+ shadowRadius: scale(14),
114
+ elevation: 4,
115
+ }
116
116
  : getShadowStyle();
117
117
 
118
118
  // Consolidate static card styles with responsive scaling
@@ -132,17 +132,17 @@ export const BoardingPassCard = ({
132
132
  styles.container,
133
133
  {
134
134
  borderRadius: scale(20),
135
- minHeight: scale(210),
135
+ minHeight: scale(240),
136
136
  },
137
137
  style
138
138
  ]}>
139
139
  {/* Top Section */}
140
140
  <View style={[
141
- styles.topSection,
142
- {
141
+ styles.topSection,
142
+ {
143
143
  backgroundColor: currentHeaderBg,
144
- paddingTop: scale(16),
145
- paddingBottom: scale(16),
144
+ paddingTop: scale(24),
145
+ paddingBottom: scale(24),
146
146
  paddingHorizontal: scale(18),
147
147
  }
148
148
  ]}>
@@ -158,7 +158,7 @@ export const BoardingPassCard = ({
158
158
  ]} />
159
159
 
160
160
  <View style={[styles.headerRow, { marginBottom: scale(16) }]}>
161
- <Typography variant="caption" style={[styles.headerText]}>CARTÃO DE EMBARQUE</Typography>
161
+ <Typography variant="labelHeavy" style={[styles.headerText]}>CARTÃO DE EMBARQUE</Typography>
162
162
  <View style={styles.headerPlaneContainer}>
163
163
  <Plane stroke={theme.colors.white} strokeWidth={2.2} size={scale(22)} />
164
164
  </View>
@@ -166,8 +166,8 @@ export const BoardingPassCard = ({
166
166
 
167
167
  <View style={styles.routeRow}>
168
168
  <View style={styles.cityCol}>
169
- <Typography variant="h1" style={[styles.cityCode]}>{originCode}</Typography>
170
- <Typography variant="caption" style={[styles.cityName]}>{originName}</Typography>
169
+ <Typography variant="display2" style={[styles.cityCode]}>{originCode}</Typography>
170
+ <Typography variant="small" style={[styles.cityName]}>{originName}</Typography>
171
171
  </View>
172
172
 
173
173
  <View style={[styles.flightLineContainer, { height: scale(30), marginHorizontal: scale(12) }]}>
@@ -178,8 +178,8 @@ export const BoardingPassCard = ({
178
178
  </View>
179
179
 
180
180
  <View style={[styles.cityCol, { alignItems: 'flex-end' }]}>
181
- <Typography variant="h1" style={[styles.cityCode]}>{destCode}</Typography>
182
- <Typography variant="caption" style={[styles.cityName]}>{destName}</Typography>
181
+ <Typography variant="display2" style={[styles.cityCode]}>{destCode}</Typography>
182
+ <Typography variant="small" style={[styles.cityName]}>{destName}</Typography>
183
183
  </View>
184
184
  </View>
185
185
  </View>
@@ -188,7 +188,7 @@ export const BoardingPassCard = ({
188
188
  <View style={[
189
189
  styles.bottomSection,
190
190
  {
191
- paddingVertical: scale(14),
191
+ paddingVertical: scale(20),
192
192
  paddingHorizontal: scale(18),
193
193
  }
194
194
  ]}>
@@ -205,8 +205,8 @@ export const BoardingPassCard = ({
205
205
  {flagIconNode ? flagIconNode : <Flag countryCode={countryName} size={scale(40)} />}
206
206
  </View>
207
207
  <View style={styles.countryContent}>
208
- <Typography variant="h2" style={[styles.countryName]}>{countryName}</Typography>
209
- <Typography variant="bodySm" style={[styles.countrySubtitle]}>{countrySubtitle}</Typography>
208
+ <Typography variant="subtitle1" style={[styles.countryName]}>{countryName}</Typography>
209
+ <Typography variant="label" style={[styles.countrySubtitle]}>{countrySubtitle}</Typography>
210
210
  </View>
211
211
  </View>
212
212
 
@@ -220,12 +220,12 @@ export const BoardingPassCard = ({
220
220
 
221
221
  <View style={[styles.footerRow, { gap: scale(14) }]}>
222
222
  <View style={styles.footerCol}>
223
- <Typography variant="caption" style={[styles.footerLabel]}>MÓDULOS</Typography>
224
- <Typography variant="h3" style={[styles.footerValue]}>{modulesCount}</Typography>
223
+ <Typography variant="labelHeavy" style={[styles.footerLabel]}>MÓDULOS</Typography>
224
+ <Typography variant="subtitle1" style={[styles.footerValue]}>{modulesCount}</Typography>
225
225
  </View>
226
226
  <View style={styles.footerCol}>
227
- <Typography variant="caption" style={[styles.footerLabel]}>FOCO</Typography>
228
- <Typography variant="h3" style={[styles.footerValue]}>{focusText}</Typography>
227
+ <Typography variant="labelHeavy" style={[styles.footerLabel]}>FOCO</Typography>
228
+ <Typography variant="subtitle1" style={[styles.footerValue]}>{focusText}</Typography>
229
229
  </View>
230
230
  </View>
231
231
  </View>
@@ -241,6 +241,10 @@ const styles = StyleSheet.create({
241
241
  minHeight: 230,
242
242
  ...theme.shadows.lg,
243
243
  },
244
+ headerText: {
245
+ color: theme.colors.white,
246
+ opacity: 0.9,
247
+ },
244
248
  topSection: {
245
249
  paddingTop: 16,
246
250
  paddingBottom: 16,
@@ -266,11 +270,6 @@ const styles = StyleSheet.create({
266
270
  headerPlaneContainer: {
267
271
  transform: [{ rotate: '0deg' }],
268
272
  },
269
- headerText: {
270
- letterSpacing: 1.2,
271
- textTransform: 'uppercase',
272
- color: 'rgba(255, 255, 255, 0.75)',
273
- },
274
273
  routeRow: {
275
274
  flexDirection: 'row',
276
275
  alignItems: 'center',
@@ -345,6 +344,10 @@ const styles = StyleSheet.create({
345
344
  countrySubtitle: {
346
345
  color: '#9a8478',
347
346
  },
347
+ footerLabel: {
348
+ color: theme.colors.brown[500],
349
+ marginBottom: 4,
350
+ },
348
351
  divider: {
349
352
  borderTopWidth: 1.5,
350
353
  borderColor: '#f3e7da',
@@ -16,7 +16,7 @@ const meta = {
16
16
  },
17
17
  tags: ['autodocs'],
18
18
  argTypes: {
19
- status: { control: 'radio', options: ['active', 'locked', 'completed', 'dark', 'active-dark'] },
19
+ status: { control: 'radio', options: ['active', 'locked', 'completed', 'dark', 'active-dark', 'completed-dark'] },
20
20
  number: { control: 'number' },
21
21
  title: { control: 'text' },
22
22
  subtitle: { control: 'text' },
@@ -101,6 +101,18 @@ export const Completed: Story = {
101
101
  },
102
102
  };
103
103
 
104
+ export const CompletedDark: Story = {
105
+ args: {
106
+ status: 'completed-dark',
107
+ number: 1,
108
+ title: 'Saudações & teranga (Dark)',
109
+ subtitle: 'Concluído',
110
+ subtext: '• 5/5',
111
+ onReviewPress: () => console.log('Rever módulo'),
112
+ onNextCountryPress: () => console.log('Ir para próximo país'),
113
+ },
114
+ };
115
+
104
116
  export const Grid: Story = {
105
117
  args: {
106
118
  status: 'active',
@@ -153,6 +165,13 @@ export const Grid: Story = {
153
165
  tagLabel="POR FAZER"
154
166
  onExamPress={() => {}}
155
167
  />
168
+ <ModuleCard
169
+ status="completed-dark"
170
+ number={4}
171
+ title="Módulo Concluído Dark"
172
+ subtitle="Concluído"
173
+ subtext="• 5/5"
174
+ />
156
175
  </View>
157
176
  ),
158
177
  };
@@ -9,7 +9,7 @@ import { useResponsive } from '../../../hooks/useResponsive';
9
9
  import { Typography } from '../../Typography/Typography';
10
10
  import React, { useRef } from 'react';
11
11
 
12
- export type ModuleCardStatus = 'active' | 'locked' | 'completed' | 'dark' | 'active-dark';
12
+ export type ModuleCardStatus = 'active' | 'locked' | 'completed' | 'dark' | 'active-dark' | 'completed-dark';
13
13
 
14
14
  /**
15
15
  * Cartão que representa uma lição ou módulo no caminho de aprendizagem.
@@ -17,7 +17,7 @@ export type ModuleCardStatus = 'active' | 'locked' | 'completed' | 'dark' | 'act
17
17
  * Regras de Animação e Clique:
18
18
  * - `active`: O cartão como um todo tem animação de escala e é clicável via `onPress`.
19
19
  * - `active-dark`: O cartão é estático. Apenas os botões internos de "Agendar" (`onSchedulePress`), "Exame" (`onExamPress`) e "Rever" (`onReviewPress`) têm animação individual quando fornecidos.
20
- * - `completed`: O cartão é estático. Apenas o ícone/botão superior direito de "Rever" (`onReviewPress`) tem animação individual.
20
+ * - `completed` / `completed-dark`: O cartão é estático. Apenas o ícone/botão superior direito de "Rever" (`onReviewPress`) tem animação individual.
21
21
  * - `locked` / `dark`: O cartão não é interativo.
22
22
  */
23
23
  export interface ModuleCardProps {
@@ -51,6 +51,10 @@ export interface ModuleCardProps {
51
51
  onExamPress?: () => void;
52
52
  /** Função de clique no botão de Rever (modo completed/active/dark). */
53
53
  onReviewPress?: () => void;
54
+ /** Função de clique no botão de Ir para o Próximo País (modo completed-dark). */
55
+ onNextCountryPress?: () => void;
56
+ /** Label do botão de próximo país. Padrão: "Seguir Viagem". */
57
+ nextCountryLabel?: string;
54
58
  }
55
59
 
56
60
  export const ModuleCard = ({
@@ -70,6 +74,8 @@ export const ModuleCard = ({
70
74
  onSchedulePress,
71
75
  onExamPress,
72
76
  onReviewPress,
77
+ onNextCountryPress,
78
+ nextCountryLabel = 'Seguir Viagem',
73
79
  }: ModuleCardProps) => {
74
80
  const { scale, scaleText, width } = useResponsive();
75
81
  const isMobileScreen = width < 400;
@@ -78,6 +84,7 @@ export const ModuleCard = ({
78
84
  const examScale = useRef(new Animated.Value(1)).current;
79
85
  const reviewScale = useRef(new Animated.Value(1)).current;
80
86
  const completedReviewScale = useRef(new Animated.Value(1)).current;
87
+ const nextCountryScale = useRef(new Animated.Value(1)).current;
81
88
  const scaleAnim = useRef(new Animated.Value(1)).current;
82
89
 
83
90
  const handlePressIn = () => {
@@ -139,6 +146,7 @@ export const ModuleCard = ({
139
146
  };
140
147
  case 'dark':
141
148
  case 'active-dark':
149
+ case 'completed-dark':
142
150
  return {
143
151
  borderColor: theme.colors.brown[800],
144
152
  borderWidth: 0,
@@ -155,6 +163,7 @@ export const ModuleCard = ({
155
163
  return theme.colors.brown[400];
156
164
  case 'dark':
157
165
  case 'active-dark':
166
+ case 'completed-dark':
158
167
  return theme.colors.white;
159
168
  default:
160
169
  return theme.colors.brown[800];
@@ -163,7 +172,7 @@ export const ModuleCard = ({
163
172
 
164
173
  const containerStyle = getContainerStyle();
165
174
  const titleStyleColor = getTextColor();
166
- const subtitleStyleColor = subtitleColor || (status === 'completed' ? theme.colors.success : status === 'locked' || status === 'dark' || status === 'active-dark' ? theme.colors.brown[300] : theme.colors.brown[500]);
175
+ const subtitleStyleColor = subtitleColor || (status === 'completed' || status === 'completed-dark' ? theme.colors.success : status === 'locked' || status === 'dark' || status === 'active-dark' ? theme.colors.brown[300] : theme.colors.brown[500]);
167
176
 
168
177
  const activeShadow = status === 'active' ? {
169
178
  shadowColor: theme.colors.primary,
@@ -184,7 +193,7 @@ export const ModuleCard = ({
184
193
  />
185
194
  );
186
195
  }
187
- if (status === 'completed') {
196
+ if (status === 'completed' || status === 'completed-dark') {
188
197
  const checkSize = isMobileScreen ? scale(16) : scale(22);
189
198
  return (
190
199
  <Badge
@@ -226,9 +235,8 @@ export const ModuleCard = ({
226
235
  const cardPaddingV = size === 'xl' ? scale(20) : size === 'lg' ? scale(16) : isMobileScreen ? scale(10) : scale(14);
227
236
  const cardPaddingH = size === 'xl' ? scale(20) : size === 'lg' ? scale(18) : isMobileScreen ? scale(12) : scale(15);
228
237
 
229
- const titleVariant = size === 'xl' ? 'h1' : size === 'lg' ? 'h2' : 'h3';
230
-
231
- const subtitleVariant = size === 'xl' ? 'bodyLg' : size === 'lg' ? 'bodyMd' : 'bodySm';
238
+ const titleVariant = size === 'xl' ? 'h4' : size === 'lg' ? 'h6' : 'subtitle1';
239
+ const subtitleVariant = size === 'xl' ? 'bodySm' : size === 'lg' ? 'caption' : 'label';
232
240
 
233
241
 
234
242
 
@@ -294,9 +302,24 @@ export const ModuleCard = ({
294
302
  )}
295
303
  </View>
296
304
 
297
- {(status === 'locked' || status === 'dark' || status === 'completed') && (
305
+ {(status === 'locked' || status === 'dark' || status === 'completed' || status === 'completed-dark') && (
298
306
  <View style={[styles.rightContent, { marginLeft: colGap }]}>
299
- {status === 'completed' && (
307
+ {status === 'completed-dark' && onNextCountryPress ? (
308
+ <Animated.View style={{ transform: [{ scale: nextCountryScale }] }}>
309
+ <Pressable
310
+ onPress={onNextCountryPress}
311
+ onPressIn={() => animateBtnIn(nextCountryScale)}
312
+ onPressOut={() => animateBtnOut(nextCountryScale)}
313
+ hitSlop={10}
314
+ >
315
+ <Tag
316
+ label={nextCountryLabel}
317
+ variant="success"
318
+ size={isMobileScreen ? 'xs' : 'sm'}
319
+ />
320
+ </Pressable>
321
+ </Animated.View>
322
+ ) : (status === 'completed' || status === 'completed-dark') ? (
300
323
  <Animated.View style={{ transform: [{ scale: completedReviewScale }] }}>
301
324
  <Pressable
302
325
  style={styles.reviewIconButton}
@@ -306,10 +329,10 @@ export const ModuleCard = ({
306
329
  hitSlop={10}
307
330
  >
308
331
  <RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={isMobileScreen ? scale(20) : scale(24)} />
309
- <Typography variant="label" style={[styles.reviewIconText, { fontSize: scaleText(9) }]}>REVER</Typography>
332
+ <Typography variant="smallHeavy" style={[styles.reviewIconText, { fontSize: scaleText(9) }]}>REVER</Typography>
310
333
  </Pressable>
311
334
  </Animated.View>
312
- )}
335
+ ) : null}
313
336
  {(status === 'locked' || status === 'dark') && (
314
337
  <Lock
315
338
  stroke={status === 'dark' ? 'rgba(255,255,255,0.4)' : '#c3b1a4'}
@@ -344,7 +367,7 @@ export const ModuleCard = ({
344
367
  style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
345
368
  >
346
369
  <CalendarPlus stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
347
- <Typography variant="label" style={{ color: theme.colors.white, fontSize: scaleText(12), fontWeight: 'bold', marginLeft: scale(6) }}>Agendar Aula</Typography>
370
+ <Typography variant="bodySmHeavy" style={{ color: theme.colors.white, marginLeft: scale(6) }}>Agendar Aula</Typography>
348
371
  </Pressable>
349
372
  </Animated.View>
350
373
  )}
@@ -359,7 +382,7 @@ export const ModuleCard = ({
359
382
  style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: theme.colors.primary, paddingHorizontal: scale(14), paddingVertical: scale(6), borderRadius: 100 }}
360
383
  >
361
384
  <PenTool stroke={theme.colors.white} strokeWidth={2.5} size={scale(14)} />
362
- <Typography variant="label" style={{ color: theme.colors.white, fontSize: scaleText(12), fontWeight: 'bold', marginLeft: scale(6) }}>Fazer Exame</Typography>
385
+ <Typography variant="bodySmHeavy" style={{ color: theme.colors.white, marginLeft: scale(6) }}>Fazer Exame</Typography>
363
386
  </Pressable>
364
387
  </Animated.View>
365
388
  )}
@@ -375,7 +398,7 @@ export const ModuleCard = ({
375
398
  hitSlop={8}
376
399
  >
377
400
  <RotateCcw stroke={theme.colors.primary} strokeWidth={2.5} size={scale(16)} />
378
- <Typography variant="label" style={[styles.reviewIconText, { color: theme.colors.primary, fontSize: scaleText(10), marginLeft: scale(4), marginTop: 0 }]}>REVER</Typography>
401
+ <Typography variant="smallHeavy" style={[styles.reviewIconText, { color: theme.colors.primary, marginLeft: scale(4), marginTop: 0 }]}>REVER</Typography>
379
402
  </Pressable>
380
403
  </Animated.View>
381
404
  )}