frontend: introduce new care actions screens
Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago
d8d5424a8ec32b325e17aa210b8b18da460390d4 Browse filesParents
fc5b05dChanged files
6
Additions
+936
Deletions
0
/dev/null → frontend/app/care-actions/activity.tsx
1 +
import {
2 +
View,
3 +
Text,
4 +
StyleSheet,
5 +
KeyboardAvoidingView,
6 +
ScrollView,
7 +
} from "react-native";
8 +
import { useState } from "react";
9 +
import { router } from "expo-router";
10 +
11 +
import { Card } from "@/components/ui/Cards";
12 +
import { Input } from "@/components/ui/Input";
13 +
import { Button } from "@/components/ui/Button";
14 +
import { Section, SectionHeader } from "@/components/ui/Sections";
15 +
import { CategoryChips, CategoryOption } from "@/components/ui/CategoryChips";
16 +
import { Spacing, Typography, Colors, Shadows } from "@/constants/theme";
17 +
import { Ionicons } from "@expo/vector-icons";
18 +
import { useCreateCareAction } from "@/hooks";
19 +
import { ActivityType } from "@/lib/api";
20 +
21 +
const ACTIVITY_TYPES: CategoryOption<ActivityType>[] = [
22 +
{ label: 'Caminhada', value: 'WALK', icon: <Ionicons name="walk-outline" size={16} color={Colors.light.tint} /> },
23 +
{ label: 'Yoga', value: 'YOGA', icon: <Ionicons name="body-outline" size={16} color={Colors.light.tint} /> },
24 +
{ label: 'Academia', value: 'GYM', icon: <Ionicons name="barbell-outline" size={16} color={Colors.light.tint} /> },
25 +
{ label: 'Meditação', value: 'MEDITATION', icon: <Ionicons name="leaf-outline" size={16} color={Colors.light.tint} /> },
26 +
{ label: 'Social', value: 'SOCIAL', icon: <Ionicons name="people-outline" size={16} color={Colors.light.tint} /> },
27 +
{ label: 'Criativo', value: 'CREATIVE', icon: <Ionicons name="color-palette-outline" size={16} color={Colors.light.tint} /> },
28 +
{ label: 'Outro', value: 'OTHER', icon: <Ionicons name="help-outline" size={16} color={Colors.light.tint} /> },
29 +
];
30 +
31 +
export default function ActivityCareAction() {
32 +
const [activityType, setActivityType] = useState<ActivityType>('WALK');
33 +
const [activityDuration, setActivityDuration] = useState('');
34 +
35 +
const createCareAction = useCreateCareAction();
36 +
37 +
const handleSave = async () => {
38 +
await createCareAction.mutateAsync({
39 +
type: 'ACTIVITY',
40 +
moment: new Date(),
41 +
activity: {
42 +
type: activityType,
43 +
duration: activityDuration ? Number(activityDuration) : undefined,
44 +
},
45 +
});
46 +
router.replace('/(tabs)/actions');
47 +
};
48 +
49 +
return (
50 +
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
51 +
<ScrollView scrollEventThrottle={16}>
52 +
<View style={styles.container}>
53 +
<View style={styles.header}>
54 +
<Text style={styles.headerTitle}>Registrar atividade</Text>
55 +
<Text style={styles.headerSubtitle}>
56 +
Qual atividade você realizou?
57 +
</Text>
58 +
</View>
59 +
60 +
<Section>
61 +
<SectionHeader title="Tipo de atividade" />
62 +
<CategoryChips
63 +
options={ACTIVITY_TYPES}
64 +
active={activityType}
65 +
onChange={setActivityType}
66 +
/>
67 +
</Section>
68 +
69 +
<Section>
70 +
<SectionHeader title="Duração (min, opcional)" />
71 +
<Card style={{ padding: Spacing.cardGap }}>
72 +
<Input
73 +
label="Duração em minutos"
74 +
value={activityDuration}
75 +
onChangeText={setActivityDuration}
76 +
placeholder="Ex: 30"
77 +
keyboardType="numeric"
78 +
variant="darkGhost"
79 +
/>
80 +
</Card>
81 +
</Section>
82 +
83 +
<Button
84 +
title="Salvar"
85 +
onPress={handleSave}
86 +
loading={createCareAction.isPending}
87 +
style={{ ...Shadows.lg }}
88 +
/>
89 +
</View>
90 +
</ScrollView>
91 +
</KeyboardAvoidingView>
92 +
);
93 +
}
94 +
95 +
const styles = StyleSheet.create({
96 +
keyboardView: {
97 +
flex: 1,
98 +
},
99 +
container: {
100 +
gap: 24,
101 +
paddingHorizontal: Spacing.containerPadding,
102 +
paddingVertical: Spacing.sectionGap,
103 +
marginBottom: 50,
104 +
},
105 +
header: {
106 +
gap: 8,
107 +
},
108 +
headerTitle: {
109 +
...Typography.headlineMd,
110 +
},
111 +
headerSubtitle: {
112 +
...Typography.bodyMd,
113 +
color: Colors.light.textSecondary,
114 +
},
115 +
});
/dev/null → frontend/app/care-actions/appointment.tsx
1 +
import {
2 +
View,
3 +
Text,
4 +
StyleSheet,
5 +
KeyboardAvoidingView,
6 +
ScrollView,
7 +
TouchableOpacity,
8 +
} from "react-native";
9 +
import { useState } from "react";
10 +
import { router } from "expo-router";
11 +
12 +
import { Card } from "@/components/ui/Cards";
13 +
import { TextArea } from "@/components/ui/Input";
14 +
import { Button } from "@/components/ui/Button";
15 +
import { Section, SectionHeader } from "@/components/ui/Sections";
16 +
import { CategoryChips, CategoryOption } from "@/components/ui/CategoryChips";
17 +
import { Spacing, Typography, Colors, BorderRadius, Shadows } from "@/constants/theme";
18 +
import { Ionicons } from "@expo/vector-icons";
19 +
import { useCreateCareAction } from "@/hooks";
20 +
import { AppointmentType } from "@/lib/api";
21 +
22 +
const APPOINTMENT_TYPES: CategoryOption<AppointmentType>[] = [
23 +
{ label: 'Analista', value: 'ANALYST', icon: <Ionicons name="person-outline" size={16} color={Colors.light.tint} /> },
24 +
{ label: 'Psiquiatra', value: 'PSYCHIATRIST', icon: <Ionicons name="medical-outline" size={16} color={Colors.light.tint} /> },
25 +
{ label: 'Clínico', value: 'GP', icon: <Ionicons name="fitness-outline" size={16} color={Colors.light.tint} /> },
26 +
{ label: 'Nutricionista', value: 'NUTRITIONIST', icon: <Ionicons name="nutrition-outline" size={16} color={Colors.light.tint} /> },
27 +
{ label: 'Outro', value: 'OTHER', icon: <Ionicons name="help-outline" size={16} color={Colors.light.tint} /> },
28 +
];
29 +
30 +
const DURATIONS = [30, 45, 50, 60];
31 +
32 +
export default function AppointmentCareAction() {
33 +
const [appointmentType, setAppointmentType] = useState<AppointmentType>('ANALYST');
34 +
const [appointmentDuration, setAppointmentDuration] = useState<number>(50);
35 +
const [appointmentNote, setAppointmentNote] = useState('');
36 +
37 +
const createCareAction = useCreateCareAction();
38 +
39 +
const handleSave = async () => {
40 +
await createCareAction.mutateAsync({
41 +
type: 'APPOINTMENT',
42 +
moment: new Date(),
43 +
appointment: {
44 +
type: appointmentType,
45 +
duration: appointmentDuration,
46 +
note: appointmentNote || undefined,
47 +
},
48 +
});
49 +
router.replace('/care-actions/post-appointment');
50 +
};
51 +
52 +
return (
53 +
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
54 +
<ScrollView scrollEventThrottle={16}>
55 +
<View style={styles.container}>
56 +
<View style={styles.header}>
57 +
<Text style={styles.headerTitle}>Registrar consulta</Text>
58 +
<Text style={styles.headerSubtitle}>
59 +
Informe os detalhes da consulta ou sessão
60 +
</Text>
61 +
</View>
62 +
63 +
<Section>
64 +
<SectionHeader title="Tipo de consulta" />
65 +
<CategoryChips
66 +
options={APPOINTMENT_TYPES}
67 +
active={appointmentType}
68 +
onChange={setAppointmentType}
69 +
/>
70 +
</Section>
71 +
72 +
<Section>
73 +
<SectionHeader title="Duração (min)" />
74 +
<View style={styles.durationRow}>
75 +
{DURATIONS.map((d) => (
76 +
<TouchableOpacity
77 +
key={d}
78 +
style={[
79 +
styles.durationChip,
80 +
appointmentDuration === d && styles.durationChipActive,
81 +
]}
82 +
onPress={() => setAppointmentDuration(d)}
83 +
activeOpacity={0.7}
84 +
>
85 +
<Text
86 +
style={[
87 +
styles.durationLabel,
88 +
appointmentDuration === d && styles.durationLabelActive,
89 +
]}
90 +
>
91 +
{d}
92 +
</Text>
93 +
</TouchableOpacity>
94 +
))}
95 +
</View>
96 +
</Section>
97 +
98 +
<Section>
99 +
<SectionHeader title="Notas (opcional)" />
100 +
<Card style={{ padding: Spacing.cardGap }}>
101 +
<TextArea
102 +
label="Como foi a consulta?"
103 +
variant="darkGhost"
104 +
value={appointmentNote}
105 +
onChangeText={setAppointmentNote}
106 +
minRows={4}
107 +
placeholder="Descreva brevemente..."
108 +
/>
109 +
</Card>
110 +
</Section>
111 +
112 +
<Button
113 +
title="Salvar"
114 +
onPress={handleSave}
115 +
loading={createCareAction.isPending}
116 +
style={{ ...Shadows.lg }}
117 +
/>
118 +
</View>
119 +
</ScrollView>
120 +
</KeyboardAvoidingView>
121 +
);
122 +
}
123 +
124 +
const styles = StyleSheet.create({
125 +
keyboardView: {
126 +
flex: 1,
127 +
},
128 +
container: {
129 +
gap: 24,
130 +
paddingHorizontal: Spacing.containerPadding,
131 +
paddingVertical: Spacing.sectionGap,
132 +
marginBottom: 50,
133 +
},
134 +
header: {
135 +
gap: 8,
136 +
},
137 +
headerTitle: {
138 +
...Typography.headlineMd,
139 +
},
140 +
headerSubtitle: {
141 +
...Typography.bodyMd,
142 +
color: Colors.light.textSecondary,
143 +
},
144 +
durationRow: {
145 +
flexDirection: 'row',
146 +
gap: 10,
147 +
flexWrap: 'wrap',
148 +
},
149 +
durationChip: {
150 +
paddingHorizontal: 20,
151 +
paddingVertical: 12,
152 +
borderRadius: BorderRadius.full,
153 +
backgroundColor: '#fff',
154 +
borderWidth: 1.5,
155 +
borderColor: Colors.light.divider,
156 +
minWidth: 64,
157 +
alignItems: 'center',
158 +
},
159 +
durationChipActive: {
160 +
backgroundColor: Colors.light.tint + '1A',
161 +
borderColor: Colors.light.tint,
162 +
},
163 +
durationLabel: {
164 +
fontSize: 16,
165 +
fontWeight: '600',
166 +
color: Colors.light.text,
167 +
},
168 +
durationLabelActive: {
169 +
color: Colors.light.text,
170 +
},
171 +
});
/dev/null → frontend/app/care-actions/medicine-new.tsx
1 +
import {
2 +
View,
3 +
Text,
4 +
StyleSheet,
5 +
KeyboardAvoidingView,
6 +
ScrollView,
7 +
TouchableOpacity,
8 +
} from "react-native";
9 +
import { useState } from "react";
10 +
import { router } from "expo-router";
11 +
12 +
import { Card } from "@/components/ui/Cards";
13 +
import { Input } from "@/components/ui/Input";
14 +
import { Button } from "@/components/ui/Button";
15 +
import { Section, SectionHeader } from "@/components/ui/Sections";
16 +
import { Spacing, Typography, Colors, BorderRadius, Shadows } from "@/constants/theme";
17 +
import { TimePicker } from "@/components/ui/TimePicker";
18 +
import { useCreateCareAction } from "@/hooks";
19 +
import { MedicinePeriodicity } from "@/lib/api";
20 +
21 +
const PERIODICITY_OPTIONS: { label: string; value: MedicinePeriodicity }[] = [
22 +
{ label: 'Uma vez', value: 'ONCE' },
23 +
{ label: 'Diário', value: 'DAILY' },
24 +
{ label: '2x ao dia', value: 'TWICE_DAILY' },
25 +
{ label: '3x ao dia', value: 'THREE_TIMES_DAILY' },
26 +
{ label: 'Semanal', value: 'WEEKLY' },
27 +
{ label: 'Quinzenal', value: 'BIWEEKLY' },
28 +
{ label: 'Mensal', value: 'MONTHLY' },
29 +
];
30 +
31 +
function getTimePickerCount(p: MedicinePeriodicity): number {
32 +
switch (p) {
33 +
case 'ONCE': return 0;
34 +
case 'DAILY': return 1;
35 +
case 'TWICE_DAILY': return 2;
36 +
case 'THREE_TIMES_DAILY': return 3;
37 +
case 'WEEKLY':
38 +
case 'BIWEEKLY':
39 +
case 'MONTHLY': return 1;
40 +
}
41 +
}
42 +
43 +
export default function MedicineNewCareAction() {
44 +
const [medicineName, setMedicineName] = useState('');
45 +
const [medicineDosage, setMedicineDosage] = useState('');
46 +
const [medicinePeriodicity, setMedicinePeriodicity] = useState<MedicinePeriodicity>('ONCE');
47 +
const [scheduledTimes, setScheduledTimes] = useState<Date[]>([]);
48 +
49 +
const createCareAction = useCreateCareAction();
50 +
51 +
const handleSave = async () => {
52 +
const count = getTimePickerCount(medicinePeriodicity);
53 +
const scheduledAt = count > 0
54 +
? scheduledTimes.slice(0, count)
55 +
.map(d => `${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`)
56 +
.join(',')
57 +
: undefined;
58 +
59 +
await createCareAction.mutateAsync({
60 +
type: 'MEDICINE',
61 +
moment: new Date(),
62 +
medicine: {
63 +
name: medicineName,
64 +
dosage: medicineDosage,
65 +
periodicity: medicinePeriodicity,
66 +
scheduledAt,
67 +
},
68 +
});
69 +
router.replace('/(tabs)/actions');
70 +
};
71 +
72 +
return (
73 +
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
74 +
<ScrollView scrollEventThrottle={16}>
75 +
<View style={styles.container}>
76 +
<View style={styles.header}>
77 +
<Text style={styles.headerTitle}>Novo medicamento</Text>
78 +
<Text style={styles.headerSubtitle}>
79 +
Informe os detalhes do medicamento
80 +
</Text>
81 +
</View>
82 +
83 +
<Section>
84 +
<SectionHeader title="Detalhes" />
85 +
<Card style={{ padding: Spacing.cardGap }}>
86 +
<Input
87 +
label="Nome"
88 +
value={medicineName}
89 +
onChangeText={setMedicineName}
90 +
placeholder="Ex: Fluoxetina"
91 +
variant="darkGhost"
92 +
/>
93 +
<Input
94 +
label="Dosagem"
95 +
value={medicineDosage}
96 +
onChangeText={setMedicineDosage}
97 +
placeholder="Ex: 20mg"
98 +
variant="darkGhost"
99 +
/>
100 +
</Card>
101 +
</Section>
102 +
103 +
<Section>
104 +
<SectionHeader title="Frequência" />
105 +
<View style={styles.periodicityRow}>
106 +
{PERIODICITY_OPTIONS.map((opt) => (
107 +
<TouchableOpacity
108 +
key={opt.value}
109 +
style={[
110 +
styles.periodicityChip,
111 +
medicinePeriodicity === opt.value && styles.periodicityChipActive,
112 +
]}
113 +
onPress={() => {
114 +
setMedicinePeriodicity(opt.value);
115 +
const count = getTimePickerCount(opt.value);
116 +
setScheduledTimes(prev =>
117 +
count === 0 ? [] :
118 +
count > prev.length ? [...prev, ...Array(count - prev.length).fill(new Date())] :
119 +
prev.slice(0, count)
120 +
);
121 +
}}
122 +
activeOpacity={0.7}
123 +
>
124 +
<Text
125 +
style={[
126 +
styles.periodicityChipLabel,
127 +
medicinePeriodicity === opt.value && styles.periodicityChipLabelActive,
128 +
]}
129 +
>
130 +
{opt.label}
131 +
</Text>
132 +
</TouchableOpacity>
133 +
))}
134 +
</View>
135 +
</Section>
136 +
137 +
{scheduledTimes.length > 0 && (
138 +
<Section>
139 +
<SectionHeader title={scheduledTimes.length === 1 ? 'Horário' : 'Horários'} />
140 +
{scheduledTimes.map((t, i) => (
141 +
<View style={{ marginTop: 10 }} key={i}>
142 +
<TimePicker
143 +
value={t}
144 +
onChange={(date) =>
145 +
setScheduledTimes(prev => prev.map((v, idx) => idx === i ? date : v))
146 +
}
147 +
/>
148 +
</View>
149 +
))}
150 +
</Section>
151 +
)}
152 +
153 +
<Button
154 +
title="Salvar"
155 +
onPress={handleSave}
156 +
loading={createCareAction.isPending}
157 +
disabled={!medicineName || !medicineDosage}
158 +
/>
159 +
</View>
160 +
</ScrollView>
161 +
</KeyboardAvoidingView>
162 +
);
163 +
}
164 +
165 +
const styles = StyleSheet.create({
166 +
keyboardView: {
167 +
flex: 1,
168 +
},
169 +
container: {
170 +
gap: 24,
171 +
paddingHorizontal: Spacing.containerPadding,
172 +
paddingVertical: Spacing.sectionGap,
173 +
marginBottom: 50,
174 +
},
175 +
header: {
176 +
gap: 8,
177 +
},
178 +
headerTitle: {
179 +
...Typography.headlineMd,
180 +
},
181 +
headerSubtitle: {
182 +
...Typography.bodyMd,
183 +
color: Colors.light.textSecondary,
184 +
},
185 +
periodicityRow: {
186 +
flexDirection: 'row',
187 +
flexWrap: 'wrap',
188 +
gap: 8,
189 +
},
190 +
periodicityChip: {
191 +
paddingHorizontal: 16,
192 +
paddingVertical: 10,
193 +
borderRadius: BorderRadius.full,
194 +
backgroundColor: '#fff',
195 +
borderWidth: 1.5,
196 +
borderColor: Colors.light.divider,
197 +
},
198 +
periodicityChipActive: {
199 +
backgroundColor: Colors.light.tint + '1A',
200 +
borderColor: Colors.light.tint,
201 +
},
202 +
periodicityChipLabel: {
203 +
fontSize: 14,
204 +
fontWeight: '500',
205 +
color: Colors.light.text,
206 +
},
207 +
periodicityChipLabelActive: {
208 +
fontWeight: '600',
209 +
color: Colors.light.text,
210 +
},
211 +
});
/dev/null → frontend/app/care-actions/medicine.tsx
1 +
import {
2 +
View,
3 +
Text,
4 +
StyleSheet,
5 +
KeyboardAvoidingView,
6 +
ScrollView,
7 +
TouchableOpacity,
8 +
} from "react-native";
9 +
import { router } from "expo-router";
10 +
11 +
import { Button } from "@/components/ui/Button";
12 +
import { Section, SectionHeader } from "@/components/ui/Sections";
13 +
import { Spacing, Typography, Colors, BorderRadius, Shadows } from "@/constants/theme";
14 +
import { Ionicons } from "@expo/vector-icons";
15 +
import { useMedicineRegimens, useCreateCareAction } from "@/hooks";
16 +
17 +
export default function MedicineCareAction() {
18 +
const { data: regimensData, isLoading: isLoadingRegimens } = useMedicineRegimens();
19 +
const createCareAction = useCreateCareAction();
20 +
21 +
const regimens = regimensData?.regimens ?? [];
22 +
23 +
const handleSelectRegimen = async (regimenId: number) => {
24 +
await createCareAction.mutateAsync({
25 +
type: 'MEDICINE',
26 +
moment: new Date(),
27 +
regimenId,
28 +
});
29 +
router.replace('/(tabs)/actions');
30 +
};
31 +
32 +
return (
33 +
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
34 +
<ScrollView scrollEventThrottle={16}>
35 +
<View style={styles.container}>
36 +
<View style={styles.header}>
37 +
<Text style={styles.headerTitle}>Qual medicamento?</Text>
38 +
<Text style={styles.headerSubtitle}>
39 +
Selecione um medicamento existente ou registre um novo
40 +
</Text>
41 +
</View>
42 +
43 +
{isLoadingRegimens && (
44 +
<Text style={styles.loadingText}>Carregando medicamentos...</Text>
45 +
)}
46 +
47 +
{!isLoadingRegimens && regimens.length > 0 && (
48 +
<Section>
49 +
<SectionHeader title="Medicamentos cadastrados" />
50 +
{regimens.map((regimen) => (
51 +
<TouchableOpacity
52 +
key={regimen.id}
53 +
style={styles.regimenChip}
54 +
activeOpacity={0.7}
55 +
onPress={() => handleSelectRegimen(regimen.id)}
56 +
disabled={createCareAction.isPending}
57 +
>
58 +
<Ionicons name="bandage-outline" size={18} color={Colors.light.tint} />
59 +
<View style={styles.regimenChipText}>
60 +
<Text style={styles.regimenChipName}>{regimen.name}</Text>
61 +
<Text style={styles.regimenChipDosage}>{regimen.dosage}</Text>
62 +
</View>
63 +
<Ionicons name="chevron-forward" size={16} color={Colors.light.disabled} />
64 +
</TouchableOpacity>
65 +
))}
66 +
</Section>
67 +
)}
68 +
69 +
<Button
70 +
title="Cadastrar outro medicamento"
71 +
variant="dashed"
72 +
onPress={() => router.push('/care-actions/medicine-new')}
73 +
/>
74 +
</View>
75 +
</ScrollView>
76 +
</KeyboardAvoidingView>
77 +
);
78 +
}
79 +
80 +
const styles = StyleSheet.create({
81 +
keyboardView: {
82 +
flex: 1,
83 +
},
84 +
container: {
85 +
gap: 24,
86 +
paddingHorizontal: Spacing.containerPadding,
87 +
paddingVertical: Spacing.sectionGap,
88 +
marginBottom: 50,
89 +
},
90 +
header: {
91 +
gap: 8,
92 +
},
93 +
headerTitle: {
94 +
...Typography.headlineMd,
95 +
},
96 +
headerSubtitle: {
97 +
...Typography.bodyMd,
98 +
color: Colors.light.textSecondary,
99 +
},
100 +
loadingText: {
101 +
...Typography.bodyMd,
102 +
color: Colors.light.textSecondary,
103 +
textAlign: 'center',
104 +
},
105 +
regimenChip: {
106 +
flexDirection: 'row',
107 +
alignItems: 'center',
108 +
backgroundColor: '#fff',
109 +
borderRadius: BorderRadius.lg,
110 +
padding: Spacing.cardGap,
111 +
borderWidth: 1,
112 +
borderColor: Colors.light.divider,
113 +
gap: 12,
114 +
marginBottom: 8,
115 +
...Shadows.sm,
116 +
},
117 +
regimenChipText: {
118 +
flex: 1,
119 +
gap: 2,
120 +
},
121 +
regimenChipName: {
122 +
...Typography.bodyMd,
123 +
fontWeight: '600',
124 +
},
125 +
regimenChipDosage: {
126 +
...Typography.labelSm,
127 +
color: Colors.light.textSecondary,
128 +
},
129 +
});
/dev/null → frontend/app/care-actions/new.tsx
1 +
import {
2 +
View,
3 +
Text,
4 +
StyleSheet,
5 +
KeyboardAvoidingView,
6 +
ScrollView,
7 +
TouchableOpacity,
8 +
} from "react-native";
9 +
import { router } from "expo-router";
10 +
11 +
import { Spacing, Typography, Colors, BorderRadius, Shadows } from "@/constants/theme";
12 +
import { Ionicons } from "@expo/vector-icons";
13 +
14 +
export default function NewCareAction() {
15 +
return (
16 +
<KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
17 +
<ScrollView scrollEventThrottle={16}>
18 +
<View style={styles.container}>
19 +
<View style={styles.header}>
20 +
<Text style={styles.headerTitle}>Que tipo de ação?</Text>
21 +
<Text style={styles.headerSubtitle}>
22 +
Selecione o tipo de ação de cuidado que deseja registrar
23 +
</Text>
24 +
</View>
25 +
26 +
<TouchableOpacity
27 +
style={styles.typeCard}
28 +
activeOpacity={0.7}
29 +
onPress={() => router.push('/care-actions/medicine')}
30 +
>
31 +
<View style={[styles.typeIcon, { backgroundColor: '#E7FDEF' }]}>
32 +
<Ionicons name="bandage-outline" size={24} color="#13EC5B" />
33 +
</View>
34 +
<View style={styles.typeTextWrap}>
35 +
<Text style={styles.typeTitle}>Medicamento</Text>
36 +
<Text style={styles.typeDesc}>Registre a tomada de um medicamento</Text>
37 +
</View>
38 +
<Ionicons name="chevron-forward" size={20} color={Colors.light.disabled} />
39 +
</TouchableOpacity>
40 +
41 +
<TouchableOpacity
42 +
style={styles.typeCard}
43 +
activeOpacity={0.7}
44 +
onPress={() => router.push('/care-actions/appointment')}
45 +
>
46 +
<View style={[styles.typeIcon, { backgroundColor: '#EFF6FF' }]}>
47 +
<Ionicons name="medkit-outline" size={24} color="#2563EB" />
48 +
</View>
49 +
<View style={styles.typeTextWrap}>
50 +
<Text style={styles.typeTitle}>Consulta</Text>
51 +
<Text style={styles.typeDesc}>Registre uma consulta ou sessão</Text>
52 +
</View>
53 +
<Ionicons name="chevron-forward" size={20} color={Colors.light.disabled} />
54 +
</TouchableOpacity>
55 +
56 +
<TouchableOpacity
57 +
style={styles.typeCard}
58 +
activeOpacity={0.7}
59 +
onPress={() => router.push('/care-actions/activity')}
60 +
>
61 +
<View style={[styles.typeIcon, { backgroundColor: '#FFF7ED' }]}>
62 +
<Ionicons name="walk-outline" size={24} color="#EA580C" />
63 +
</View>
64 +
<View style={styles.typeTextWrap}>
65 +
<Text style={styles.typeTitle}>Atividade</Text>
66 +
<Text style={styles.typeDesc}>Registre uma atividade física ou mental</Text>
67 +
</View>
68 +
<Ionicons name="chevron-forward" size={20} color={Colors.light.disabled} />
69 +
</TouchableOpacity>
70 +
</View>
71 +
</ScrollView>
72 +
</KeyboardAvoidingView>
73 +
);
74 +
}
75 +
76 +
const styles = StyleSheet.create({
77 +
keyboardView: {
78 +
flex: 1,
79 +
},
80 +
container: {
81 +
gap: 24,
82 +
paddingHorizontal: Spacing.containerPadding,
83 +
paddingVertical: Spacing.sectionGap,
84 +
marginBottom: 50,
85 +
},
86 +
header: {
87 +
gap: 8,
88 +
},
89 +
headerTitle: {
90 +
...Typography.headlineMd,
91 +
},
92 +
headerSubtitle: {
93 +
...Typography.bodyMd,
94 +
color: Colors.light.textSecondary,
95 +
},
96 +
typeCard: {
97 +
flexDirection: 'row',
98 +
alignItems: 'center',
99 +
backgroundColor: '#fff',
100 +
borderRadius: BorderRadius.lg,
101 +
padding: Spacing.cardGap,
102 +
borderWidth: 1,
103 +
borderColor: Colors.light.divider,
104 +
gap: 12,
105 +
...Shadows.sm,
106 +
},
107 +
typeIcon: {
108 +
width: 48,
109 +
height: 48,
110 +
borderRadius: BorderRadius.md,
111 +
alignItems: 'center',
112 +
justifyContent: 'center',
113 +
},
114 +
typeTextWrap: {
115 +
flex: 1,
116 +
gap: 2,
117 +
},
118 +
typeTitle: {
119 +
...Typography.bodyLg,
120 +
},
121 +
typeDesc: {
122 +
...Typography.labelSm,
123 +
color: Colors.light.textSecondary,
124 +
},
125 +
});
/dev/null → frontend/app/care-actions/post-appointment.tsx
1 +
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from "react-native";
2 +
import { router } from "expo-router";
3 +
import { Ionicons } from "@expo/vector-icons";
4 +
import { SafeAreaView } from "react-native-safe-area-context";
5 +
6 +
import { Button } from "@/components/ui/Button";
7 +
import { Card } from "@/components/ui/Cards";
8 +
import { Colors, Spacing, Typography, BorderRadius, Shadows } from "@/constants/theme";
9 +
10 +
export default function PostAppointment() {
11 +
return (
12 +
<SafeAreaView style={styles.safe}>
13 +
<ScrollView
14 +
contentContainerStyle={styles.scrollContent}
15 +
scrollEventThrottle={16}
16 +
showsVerticalScrollIndicator={false}
17 +
>
18 +
{/* Confirmation block */}
19 +
<View style={styles.confirmationBlock}>
20 +
<View style={styles.outerRing}>
21 +
<View style={styles.innerCircle}>
22 +
<Ionicons name="checkmark" size={32} color="#fff" />
23 +
</View>
24 +
</View>
25 +
<Text style={styles.confirmTitle}>Consulta Salva!</Text>
26 +
<Text style={styles.confirmSubtitle}>
27 +
Sua consulta foi registrada com sucesso.
28 +
</Text>
29 +
</View>
30 +
31 +
{/* Follow-up card */}
32 +
<Card style={styles.followUpCard}>
33 +
<Text style={styles.followUpTitle}>Algo surgiu na sessão?</Text>
34 +
<Text style={styles.followUpSubtitle}>
35 +
Aproveite para registrar enquanto está fresco
36 +
</Text>
37 +
38 +
<TouchableOpacity
39 +
style={styles.optionRow}
40 +
activeOpacity={0.7}
41 +
onPress={() => router.replace("/triggers/new?category=THERAPY")}
42 +
>
43 +
<View style={[styles.optionIcon, { backgroundColor: "#ede9fe" }]}>
44 +
<Ionicons name="flash-outline" size={22} color="#7c3aed" />
45 +
</View>
46 +
<View style={styles.optionText}>
47 +
<Text style={styles.optionTitle}>Criar um gatilho</Text>
48 +
<Text style={styles.optionDesc}>
49 +
Registre algo que emergiu na sessão
50 +
</Text>
51 +
</View>
52 +
<Ionicons
53 +
name="chevron-forward"
54 +
size={18}
55 +
color={Colors.light.disabled}
56 +
/>
57 +
</TouchableOpacity>
58 +
59 +
<TouchableOpacity
60 +
style={styles.optionRow}
61 +
activeOpacity={0.7}
62 +
onPress={() => router.replace("/new/entry")}
63 +
>
64 +
<View style={[styles.optionIcon, { backgroundColor: "#fef9c3" }]}>
65 +
<Ionicons name="heart-outline" size={22} color="#ca8a04" />
66 +
</View>
67 +
<View style={styles.optionText}>
68 +
<Text style={styles.optionTitle}>Registrar humor</Text>
69 +
<Text style={styles.optionDesc}>
70 +
Como você está se sentindo agora?
71 +
</Text>
72 +
</View>
73 +
<Ionicons
74 +
name="chevron-forward"
75 +
size={18}
76 +
color={Colors.light.disabled}
77 +
/>
78 +
</TouchableOpacity>
79 +
</Card>
80 +
81 +
{/* Concluir ghost button */}
82 +
<Button
83 +
title="Concluir"
84 +
variant="ghost"
85 +
onPress={() => router.replace("/(tabs)/actions")}
86 +
style={styles.concludeButton}
87 +
/>
88 +
</ScrollView>
89 +
</SafeAreaView>
90 +
);
91 +
}
92 +
93 +
const styles = StyleSheet.create({
94 +
safe: {
95 +
flex: 1,
96 +
backgroundColor: Colors.light.background,
97 +
},
98 +
scrollContent: {
99 +
paddingHorizontal: Spacing.containerPadding,
100 +
paddingTop: 48,
101 +
paddingBottom: 36,
102 +
},
103 +
confirmationBlock: {
104 +
alignItems: "center",
105 +
marginBottom: Spacing.sectionGap,
106 +
gap: 12,
107 +
},
108 +
outerRing: {
109 +
width: 88,
110 +
height: 88,
111 +
borderRadius: 44,
112 +
backgroundColor: "#E7FDEF",
113 +
alignItems: "center",
114 +
justifyContent: "center",
115 +
},
116 +
innerCircle: {
117 +
width: 64,
118 +
height: 64,
119 +
borderRadius: 32,
120 +
backgroundColor: Colors.light.tint,
121 +
alignItems: "center",
122 +
justifyContent: "center",
123 +
},
124 +
confirmTitle: {
125 +
...Typography.headlineMd,
126 +
fontWeight: "700",
127 +
color: Colors.light.text,
128 +
textAlign: "center",
129 +
},
130 +
confirmSubtitle: {
131 +
...Typography.bodyMd,
132 +
color: Colors.light.textSecondary,
133 +
textAlign: "center",
134 +
},
135 +
followUpCard: {
136 +
padding: Spacing.cardGap,
137 +
marginBottom: Spacing.sectionGap,
138 +
gap: 12,
139 +
},
140 +
followUpTitle: {
141 +
...Typography.headlineMd,
142 +
fontWeight: "700",
143 +
color: Colors.light.text,
144 +
marginBottom: 2,
145 +
},
146 +
followUpSubtitle: {
147 +
...Typography.bodyMd,
148 +
color: Colors.light.textSecondary,
149 +
marginBottom: 8,
150 +
},
151 +
optionRow: {
152 +
flexDirection: "row",
153 +
alignItems: "center",
154 +
padding: Spacing.cardGap,
155 +
backgroundColor: "#fff",
156 +
borderRadius: BorderRadius.lg,
157 +
borderWidth: 1,
158 +
borderColor: Colors.light.divider,
159 +
gap: 12,
160 +
...Shadows.sm,
161 +
},
162 +
optionIcon: {
163 +
width: 44,
164 +
height: 44,
165 +
borderRadius: BorderRadius.md,
166 +
alignItems: "center",
167 +
justifyContent: "center",
168 +
},
169 +
optionText: {
170 +
flex: 1,
171 +
gap: 2,
172 +
},
173 +
optionTitle: {
174 +
...Typography.bodyMd,
175 +
fontWeight: "600",
176 +
color: Colors.light.text,
177 +
},
178 +
optionDesc: {
179 +
...Typography.labelSm,
180 +
color: Colors.light.textSecondary,
181 +
},
182 +
concludeButton: {
183 +
marginTop: 8,
184 +
},
185 +
});