eletrotupi / tcc/ commit / c7abb1f

frontend/auth: add the terms and onboarding flows

Signing up now asks for the terms up front and links to the page, and a
first run after activation gets a short skippable tour. Both stay
reachable from settings
Pedro Lucas Porcellis porcellis@eletrotupi.com · 11 days ago c7abb1f398f8a1bc181dc8f1933a67881fa6b9d5 Browse files
Parents 6bdc28a
Changed files
3
Additions
+247
Deletions
0
frontend/app/(tabs)/settings.tsx
1 1
import {
2 2
  TouchableOpacity,
3 3
  StyleSheet,
4 4
  View,
5 5
  Switch,
302 302
              name="file-download"
303 303
              size={24}
304 304
              color={Colors.light.textSecondary}
305 305
            />
306 306
          </TouchableOpacity>
307 +

308 +
          <View style={styles.divider} />
309 +

310 +
          <TouchableOpacity
311 +
            style={{
312 +
              flexDirection: "row",
313 +
              alignItems: "center",
314 +
              justifyContent: "space-between",
315 +
            }}
316 +
            onPress={() => router.push("/terms")}
317 +
          >
318 +
            <View style={{ flexDirection: "row", alignItems: "center" }}>
319 +
              <MaterialIcons
320 +
                name="description"
321 +
                size={24}
322 +
                color={Colors.light.textSecondary}
323 +
              />
324 +

325 +
              <Text style={[styles.rowLabel, { marginLeft: 8 }]}>
326 +
                Termos e Condições
327 +
              </Text>
328 +
            </View>
329 +

330 +
            <Ionicons name="chevron-forward" size={24} color={textColor} />
331 +
          </TouchableOpacity>
307 332
        </Card>
308 333
      </Section>
309 334

310 335
      <Button
311 336
        title="Sair da Conta"
369 394
    height: StyleSheet.hairlineWidth,
370 395
    backgroundColor: "#D1D5DB",
371 396
    marginVertical: 16,
372 397
  },
373 398
});
/dev/null → frontend/app/onboarding.tsx
1 +
import React, { useState } from 'react';
2 +
import { View, StyleSheet } from 'react-native';
3 +
import { router } from 'expo-router';
4 +
import { SafeAreaView } from 'react-native-safe-area-context';
5 +
import { Text } from '@/components/ui/Text';
6 +
import { Button } from '@/components/ui';
7 +
import { useAuth } from '@/context/AuthContext';
8 +
import { useThemeColor } from '@/hooks/use-theme-color';
9 +

10 +
const PAGES = [
11 +
  {
12 +
    title: 'Bem vindo ao nexo',
13 +
    body: 'Registre seu humor ao longo do dia, sempre depois que aconteceu. É um diário, não uma cobrança.',
14 +
  },
15 +
  {
16 +
    title: 'No seu ritmo',
17 +
    body: 'Anote quantos momentos quiser. Cada registro guarda o humor, a intensidade e o que aconteceu.',
18 +
  },
19 +
  {
20 +
    title: 'Olhe para trás',
21 +
    body: 'Com o tempo, o Histórico e a Jornada mostram padrões entre humor, sono, gatilhos e ações de cuidado.',
22 +
  },
23 +
];
24 +

25 +
export default function OnboardingScreen() {
26 +
  const [index, setIndex] = useState(0);
27 +
  const [saving, setSaving] = useState(false);
28 +
  const { completeOnboarding } = useAuth();
29 +
  const textColor = useThemeColor({}, 'text');
30 +
  const backgroundColor = useThemeColor({}, 'background');
31 +
  const tintColor = useThemeColor({}, 'tint');
32 +

33 +
  const page = PAGES[index];
34 +
  const isLast = index === PAGES.length - 1;
35 +

36 +
  const finish = async () => {
37 +
    if (saving) return;
38 +

39 +
    setSaving(true);
40 +
    try {
41 +
      await completeOnboarding();
42 +
    } catch (error) {
43 +
      console.error('Failed to complete onboarding:', error);
44 +
    } finally {
45 +
      setSaving(false);
46 +
      router.replace('/(tabs)/new');
47 +
    }
48 +
  };
49 +

50 +
  return (
51 +
    <SafeAreaView style={[styles.container, { backgroundColor }]}>
52 +
      <View style={styles.content}>
53 +
        <Text style={[styles.title, { color: textColor }]}>{page.title}</Text>
54 +
        <Text style={[styles.body, { color: textColor, opacity: 0.7 }]}>
55 +
          {page.body}
56 +
        </Text>
57 +
      </View>
58 +

59 +
      <View style={styles.dots}>
60 +
        {PAGES.map((_, i) => (
61 +
          <View
62 +
            key={i}
63 +
            style={[
64 +
              styles.dot,
65 +
              {
66 +
                backgroundColor: i === index ? tintColor : textColor,
67 +
                opacity: i === index ? 1 : 0.2,
68 +
              },
69 +
            ]}
70 +
          />
71 +
        ))}
72 +
      </View>
73 +

74 +
      <View style={styles.actions}>
75 +
        <Button
76 +
          title={isLast ? 'Começar' : 'Próximo'}
77 +
          loading={saving}
78 +
          onPress={() => (isLast ? finish() : setIndex((i) => i + 1))}
79 +
        />
80 +
        {!isLast && (
81 +
          <Button
82 +
            variant="outline"
83 +
            title="Pular"
84 +
            onPress={finish}
85 +
            disabled={saving}
86 +
          />
87 +
        )}
88 +
      </View>
89 +
    </SafeAreaView>
90 +
  );
91 +
}
92 +

93 +
const styles = StyleSheet.create({
94 +
  container: {
95 +
    flex: 1,
96 +
  },
97 +
  content: {
98 +
    flex: 1,
99 +
    justifyContent: 'center',
100 +
    paddingHorizontal: 24,
101 +
  },
102 +
  title: {
103 +
    fontSize: 28,
104 +
    fontWeight: 'bold',
105 +
    marginBottom: 12,
106 +
  },
107 +
  body: {
108 +
    fontSize: 16,
109 +
    lineHeight: 24,
110 +
  },
111 +
  dots: {
112 +
    flexDirection: 'row',
113 +
    justifyContent: 'center',
114 +
    gap: 8,
115 +
    marginBottom: 24,
116 +
  },
117 +
  dot: {
118 +
    width: 8,
119 +
    height: 8,
120 +
    borderRadius: 4,
121 +
  },
122 +
  actions: {
123 +
    gap: 12,
124 +
    paddingHorizontal: 24,
125 +
    paddingBottom: 24,
126 +
  },
127 +
});
/dev/null → frontend/app/terms.tsx
1 +
import React from 'react';
2 +
import { Linking, ScrollView, StyleSheet, TouchableOpacity } from 'react-native';
3 +
import { Text } from '@/components/ui/Text';
4 +
import { useThemeColor } from '@/hooks/use-theme-color';
5 +

6 +
const WEBSITE_URL = 'https://usenexo.xyz';
7 +

8 +
export default function TermsScreen() {
9 +
  const textColor = useThemeColor({}, 'text');
10 +
  const tintColor = useThemeColor({}, 'tint');
11 +
  const backgroundColor = useThemeColor({}, 'background');
12 +

13 +
  return (
14 +
    <ScrollView
15 +
      style={{ backgroundColor }}
16 +
      contentContainerStyle={styles.content}
17 +
      showsVerticalScrollIndicator={false}
18 +
    >
19 +
      <Text style={[styles.title, { color: textColor }]}>
20 +
        Termos e Condições
21 +
      </Text>
22 +

23 +
      <Text style={[styles.heading, { color: textColor }]}>
24 +
        Um app acadêmico
25 +
      </Text>
26 +

27 +
      <Text style={[styles.body, { color: textColor }]}>
28 +
        O nexo é um projeto acadêmico, desenvolvido como trabalho de conclusão de
29 +
        curso. Ele não é um produto comercial nem um dispositivo médico, e não
30 +
        substitui acompanhamento profissional. Em caso de crise, procure ajuda
31 +
        médica ou um serviço de emergência.
32 +
      </Text>
33 +

34 +
      <Text style={[styles.heading, { color: textColor }]}>
35 +
        Seus dados
36 +
      </Text>
37 +

38 +
      <Text style={[styles.body, { color: textColor }]}>
39 +
        Os registros que você faz aqui são seus. Usamos seus dados apenas para o
40 +
        app funcionar: montar o seu histórico, gerar a sua jornada de indicadores
41 +
        e enviar os lembretes que você pedir. Não vendemos nem compartilhamos seus
42 +
        dados com terceiros.
43 +
      </Text>
44 +

45 +
      <Text style={[styles.body, { color: textColor }]}>
46 +
        Você pode exportar tudo o que registrou a qualquer momento, e pode pedir a
47 +
        remoção da sua conta e dos seus dados. O código-fonte é auditável e aberto.
48 +
      </Text>
49 +

50 +
      <Text style={[styles.heading, { color: textColor }]}>
51 +
        Saiba mais
52 +
      </Text>
53 +

54 +
      <Text style={[styles.body, { color: textColor }]}>
55 +
        Para saber mais sobre o projeto, visite:
56 +
      </Text>
57 +

58 +
      <TouchableOpacity style={styles.linkWrapper}
59 +
                        onPress={() => Linking.openURL(WEBSITE_URL)}>
60 +
        <Text style={[styles.link, { color: tintColor }]}>{WEBSITE_URL}</Text>
61 +
      </TouchableOpacity>
62 +
    </ScrollView>
63 +
  );
64 +
}
65 +

66 +
const styles = StyleSheet.create({
67 +
  content: {
68 +
    paddingHorizontal: 24,
69 +
    paddingTop: 16,
70 +
    paddingBottom: 40,
71 +
  },
72 +
  title: {
73 +
    fontSize: 24,
74 +
    fontWeight: 'bold',
75 +
    marginBottom: 16,
76 +
  },
77 +
  heading: {
78 +
    fontSize: 18,
79 +
    fontWeight: 'bold',
80 +
    marginTop: 8,
81 +
    marginBottom: 8,
82 +
  },
83 +
  body: {
84 +
    fontSize: 16,
85 +
    lineHeight: 24,
86 +
    marginBottom: 16,
87 +
  },
88 +
  linkWrapper: {
89 +
    marginBottom: 50
90 +
  },
91 +
  link: {
92 +
    fontSize: 16,
93 +
    textDecorationLine: 'underline',
94 +
  },
95 +
});