eletrotupi / tcc/ frontend/app/sleep/new.tsxc901c35af9c84b6a29e5185545ced1129f33b8f4
4.3 KB | 151 lines Raw
1
import {
2
  View,
3
  StyleSheet,
4
  KeyboardAvoidingView,
5
  ScrollView,
6
} from "react-native";
7
import { Text } from "@/components/ui/Text";
8
import { useState } from "react";
9
import { router } from "expo-router";
10

11
import { Card } from "@/components/ui/Cards";
12
import { TextArea } from "@/components/ui/Input";
13
import { Button } from "@/components/ui/Button";
14
import { Spacing, Typography, Colors, BorderRadius } from "@/constants/theme";
15
import { Ionicons } from "@expo/vector-icons";
16
import { DatePickerField } from "@/components/ui/DatePickerField";
17
import { ScaleSlider } from "@/components/ui/ScaleSlider";
18
import { useCreateSleepRecord } from "@/hooks";
19
import { useToast } from "@/context/ToastContext";
20
import { ApiError } from "@/lib/api";
21
import { translateError } from "@/lib/errors/translations";
22

23
export default function NewSleepRecord() {
24
  const [date, setDate] = useState(new Date());
25
  const [annotations, setAnnotations] = useState("");
26
  const [average, setAverage] = useState(7.5);
27
  const createSleepRecord = useCreateSleepRecord();
28
  const { showToast } = useToast();
29

30
  const formatValue = (value: number): string => {
31
    return String(value).replaceAll(".", ",");
32
  };
33

34
  const save = async () => {
35
    try {
36
      const data = { date, annotations, average };
37
      await createSleepRecord.mutateAsync(data);
38
      router.replace("/");
39
    } catch (error) {
40
      showToast(
41
        error instanceof ApiError ? translateError(error) : 'Falha ao salvar o registro de sono',
42
        'error',
43
      );
44
    }
45
  };
46

47
  return (
48
    <KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
49
      <ScrollView scrollEventThrottle={16}>
50
        <View style={styles.container}>
51
          <View style={styles.header}>
52
            <View style={styles.innerHeaderWrapper}>
53
              <View style={[styles.headerIcon, { backgroundColor: "#EFF6FF" }]}>
54
                <Ionicons name="moon-outline" size={20} color="#2563EB" />
55
              </View>
56

57
              <View style={styles.headerTitleSubtitle}>
58
                <Text style={styles.headerTitle}>Bom descanso</Text>
59
                <Text style={styles.headerSubtitle}>
60
                  Registre sua noite de sono
61
                </Text>
62
              </View>
63
            </View>
64

65
            <Text style={styles.description}>
66
              Acompanhar seu sono ajuda a entender como seu corpo se recupera e
67
              melhora sua performance diária.
68
            </Text>
69
          </View>
70

71
          <Card style={styles.mainCard}>
72
            <DatePickerField
73
              label="Data do Registro"
74
              initialDate={date}
75
              maximumDate={new Date()}
76
              onChange={setDate}
77
            />
78

79
            <View style={{ marginVertical: 15 }}>
80
              <ScaleSlider
81
                label="Duração"
82
                value={average}
83
                onValueChange={setAverage}
84
                onValueFormat={formatValue}
85
                step={0.5}
86
                min={0.0}
87
                max={15.0}
88
                minLabel="0h"
89
                maxLabel="15h"
90
              />
91
            </View>
92

93
            <TextArea
94
              label="Notas sobre a qualidade"
95
              variant="darkGhost"
96
              onChangeText={(val) => setAnnotations(val)}
97
              value={annotations}
98
              minRows={5}
99
              maxRows={10}
100
              placeholder="Como você se sentiu ao acordar? Teve sonhos marcantes?"
101
            />
102
          </Card>
103

104
          <Button title="Salvar Registro" onPress={save} />
105
        </View>
106
      </ScrollView>
107
    </KeyboardAvoidingView>
108
  );
109
}
110

111
const styles = StyleSheet.create({
112
  container: {
113
    gap: 24,
114
    paddingHorizontal: Spacing.containerPadding,
115
    paddingVertical: Spacing.sectionGap,
116
  },
117
  mainCard: {
118
    padding: Spacing.cardGap,
119
  },
120
  // Header
121
  header: {},
122
  headerIcon: {
123
    paddingVertical: 15,
124
    paddingHorizontal: 14,
125
    width: 48,
126
    borderRadius: BorderRadius.md,
127
  },
128
  innerHeaderWrapper: {
129
    flex: 1,
130
    flexDirection: "row",
131
    alignItems: "center",
132
  },
133
  headerTitleSubtitle: {
134
    marginLeft: 10,
135
  },
136
  headerTitle: {
137
    ...Typography.headlineMd,
138
  },
139
  headerSubtitle: {
140
    ...Typography.bodyMd,
141
    color: Colors.light.textSecondary,
142
  },
143
  description: {
144
    ...Typography.bodyMd,
145
    color: Colors.light.textSecondary,
146
    marginTop: 20,
147
  },
148
  keyboardView: {
149
    flex: 1,
150
  },
151
});