eletrotupi / tcc/ commit / 3c48957

frontend: linting some specific components + sleep/new

Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago 3c48957da540557e7d3a844d97efd318222a1371 Browse files
Parents 0e69b39
Changed files
3
Additions
+86
Deletions
-76
frontend/app/sleep/new.tsx
1 1
import {
2 2
  View,
3 3
  Text,
4 4
  StyleSheet,
5 5
  KeyboardAvoidingView,
6 -
  ScrollView
7 -
} from 'react-native';
8 -
import { useEffect, useState } from 'react';
9 -
import { router } from 'expo-router';
6 +
  ScrollView,
7 +
} from "react-native";
8 +
import { useState } from "react";
9 +
import { router } from "expo-router";
10 10

11 -
import { Card } from '@/components/ui/Cards';
12 -
import { Input, 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';
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 19

20 20
export default function NewSleepRecord() {
21 -
  const [date, setDate] = useState(new Date())
22 -
  const [annotations, setAnnotations] = useState();
21 +
  const [date, setDate] = useState(new Date());
22 +
  const [annotations, setAnnotations] = useState("");
23 23
  const [average, setAverage] = useState(7.5);
24 24
  const createSleepRecord = useCreateSleepRecord();
25 25

26 26
  const formatValue = (value: number): string => {
27 -
    return String(value).replaceAll('.', ',');
27 +
    return String(value).replaceAll(".", ",");
28 28
  };
29 29

30 30
  const save = async () => {
31 31
    const data = {
32 32
      date,
33 33
      annotations,
34 -
      average
34 +
      average,
35 35
    };
36 36

37 -
    console.log("Sleep record:", data)
38 -
    await createSleepRecord.mutateAsync(data)
39 -
    router.replace("/")
37 +
    console.log("Sleep record:", data);
38 +
    await createSleepRecord.mutateAsync(data);
39 +
    router.replace("/");
40 40
  };
41 41

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

54 52
              <View style={styles.headerTitleSubtitle}>
55 53
                <Text style={styles.headerTitle}>Bom descanso</Text>
56 -
                <Text style={styles.headerSubtitle}>Registre sua noite de sono</Text>
54 +
                <Text style={styles.headerSubtitle}>
55 +
                  Registre sua noite de sono
56 +
                </Text>
57 57
              </View>
58 58
            </View>
59 59

60 60
            <Text style={styles.description}>
61 -
              Acompanhar seu sono ajuda a entender como seu corpo se recupera
62 -
              e melhora sua performance diária.
61 +
              Acompanhar seu sono ajuda a entender como seu corpo se recupera e
62 +
              melhora sua performance diária.
63 63
            </Text>
64 64
          </View>
65 65

66 66
          <Card style={styles.mainCard}>
67 67
            <DatePickerField
79 79
                onValueFormat={formatValue}
80 80
                step={0.5}
81 81
                min={0.0}
82 82
                max={15.0}
83 83
                minLabel="0h"
84 -
                maxLabel="15h" />
84 +
                maxLabel="15h"
85 +
              />
85 86
            </View>
86 87

87 88
            <TextArea
88 89
              label="Notas sobre a qualidade"
89 -
              type="text"
90 90
              variant="darkGhost"
91 91
              onChangeText={(val) => setAnnotations(val)}
92 92
              value={annotations}
93 93
              minRows={5}
94 94
              maxRows={10}
95 95
              placeholder="Como você se sentiu ao acordar? Teve sonhos marcantes?"
96 -
              style={styles.notes}
97 96
            />
98 97
          </Card>
99 98

100 99
          <Button title="Salvar Registro" onPress={save} />
101 100
        </View>
109 108
    gap: 24,
110 109
    paddingHorizontal: Spacing.containerPadding,
111 110
    paddingVertical: Spacing.sectionGap,
112 111
  },
113 112
  mainCard: {
114 -
    padding: Spacing.cardGap
113 +
    padding: Spacing.cardGap,
115 114
  },
116 115
  // Header
117 -
  header: {
118 -
  },
116 +
  header: {},
119 117
  headerIcon: {
120 118
    paddingVertical: 15,
121 119
    paddingHorizontal: 14,
122 120
    width: 48,
123 121
    borderRadius: BorderRadius.md,
124 122
  },
125 123
  innerHeaderWrapper: {
126 124
    flex: 1,
127 -
    flexDirection: 'row',
128 -
    alignItems: 'center'
125 +
    flexDirection: "row",
126 +
    alignItems: "center",
129 127
  },
130 128
  headerTitleSubtitle: {
131 -
    marginLeft: 10
129 +
    marginLeft: 10,
132 130
  },
133 131
  headerTitle: {
134 -
    ...Typography.headlineMd
132 +
    ...Typography.headlineMd,
135 133
  },
136 134
  headerSubtitle: {
137 135
    ...Typography.bodyMd,
138 -
    color: Colors.light.textSecondary
136 +
    color: Colors.light.textSecondary,
139 137
  },
140 138
  description: {
141 139
    ...Typography.bodyMd,
142 140
    color: Colors.light.textSecondary,
143 -
    marginTop: 20
141 +
    marginTop: 20,
144 142
  },
145 143
  keyboardView: {
146 144
    flex: 1,
147 145
  },
148 -
})
146 +
});
frontend/components/ui/DatePickerField.tsx
1 1
import { useState, useEffect } from "react";
2 -
import { View, Text, Pressable, StyleSheet, Platform } from "react-native";
3 -
import DateTimePicker from "@react-native-community/datetimepicker";
2 +
import { View, Text, Pressable, StyleSheet } from "react-native";
3 +
import DateTimePicker, {
4 +
  DateTimePickerEvent,
5 +
} from "@react-native-community/datetimepicker";
4 6
import { format } from "date-fns";
5 -
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
6 -
import { Colors } from '@/constants/theme';
7 -
import { Label } from '@/components/ui/Input';
7 +
import { MaterialIcons } from "@expo/vector-icons";
8 +
import { Colors } from "@/constants/theme";
9 +
import { Label } from "@/components/ui/Input";
10 +

11 +
interface DatePickerProps {
12 +
  label: string;
13 +
  initialDate?: Date;
14 +
  onChange: (date: Date) => void;
15 +
  minimumDate?: Date;
16 +
  maximumDate?: Date;
17 +
}
8 18

9 -
export default function DatePickerField({
19 +
export const DatePickerField: React.FC<DatePickerProps> = ({
10 20
  label,
11 21
  initialDate = new Date(),
12 22
  onChange,
13 23
  minimumDate,
14 24
  maximumDate,
15 -
}) {
25 +
}) => {
16 26
  const [date, setDate] = useState(initialDate);
17 27
  const [show, setShow] = useState(false);
18 28

19 29
  useEffect(() => {
20 30
    setDate(initialDate);
21 31
  }, [initialDate]);
22 32

23 33
  const openPicker = () => setShow(true);
24 34

25 -
  const handleChange = (event, selectedDate) => {
35 +
  const handleChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
26 36
    setShow(false);
27 37

28 38
    if (selectedDate) {
29 39
      setDate(selectedDate);
30 40
      if (onChange) onChange(selectedDate);
35 45

36 46
  return (
37 47
    <View>
38 48
      <Label text={label} />
39 49

40 -
      <Pressable onPress={openPicker}
41 -
                 style={({ pressed }) => [styles.container, pressed && styles.pressed]}>
50 +
      <Pressable
51 +
        onPress={openPicker}
52 +
        style={({ pressed }) => [styles.container, pressed && styles.pressed]}
53 +
      >
42 54
        <Text style={styles.dateText}>{formatted}</Text>
43 -
        <MaterialCommunityIcons name="calendar-outline" size={22} color="#6B7280" />
55 +
        <MaterialIcons name="calendar-month" size={22} color="#6B7280" />
44 56
      </Pressable>
45 57

46 58
      {show && (
47 59
        <DateTimePicker
48 60
          value={date}
53 65
          maximumDate={maximumDate}
54 66
        />
55 67
      )}
56 68
    </View>
57 69
  );
58 -
}
70 +
};
59 71

60 72
const styles = StyleSheet.create({
61 73
  label: {
62 -
    fontWeight: '500',
74 +
    fontWeight: "500",
63 75
    color: Colors.light.textSecondary,
64 76
    fontSize: 12,
65 77
    marginBottom: 8,
66 78
    letterSpacing: 0.5,
67 -
    textTransform: 'uppercase'
79 +
    textTransform: "uppercase",
68 80
  },
69 81
  container: {
70 82
    flexDirection: "row",
71 83
    alignItems: "center",
72 84
    justifyContent: "space-between",
85 97
    color: "#111827",
86 98
    fontSize: 16,
87 99
    fontWeight: "600",
88 100
  },
89 101
});
90 -

frontend/components/ui/EmojiSelectors.tsx
1 -
import React from 'react';
1 +
import React from "react";
2 2
import {
3 3
  View,
4 4
  Pressable,
5 5
  StyleSheet,
6 6
  Image,
7 7
  Text,
8 8
  ImageSourcePropType,
9 9
  ViewStyle,
10 -
} from 'react-native';
11 -
import { Spacing, Typography, BorderRadius } from '@/constants/theme';
12 -
import { useThemeColor } from '@/hooks/use-theme-color';
10 +
} from "react-native";
11 +
import { Spacing, Typography, BorderRadius } from "@/constants/theme";
12 +
import { useThemeColor } from "@/hooks/use-theme-color";
13 13

14 14
type IconSource = string | ImageSourcePropType; // either string or a svg
15 15

16 16
// Specialized variant with labels below
17 17
interface MoodSelectorProps {
18 -
  items: Array<{
18 +
  items: T<{
19 19
    id: string;
20 20
    icon: IconSource;
21 21
    label: string;
22 22
  }>;
23 23
  value?: string;
31 31
  value,
32 32
  onSelect,
33 33
  disabled = false,
34 34
  style,
35 35
}) => {
36 -
  const accentBlueColor = useThemeColor({}, 'accentBlue');
37 -
  const tintColor = useThemeColor({}, 'tint');
38 -
  const surfaceColor = useThemeColor({}, 'surface');
39 -
  const dividerColor = useThemeColor({}, 'divider');
40 -
  const textSecondaryColor = useThemeColor({}, 'textSecondary');
36 +
  const accentBlueColor = useThemeColor({}, "accentBlue");
37 +
  const tintColor = useThemeColor({}, "tint");
38 +
  const surfaceColor = useThemeColor({}, "surface");
39 +
  const dividerColor = useThemeColor({}, "divider");
40 +
  const textSecondaryColor = useThemeColor({}, "textSecondary");
41 41

42 42
  const styles = StyleSheet.create({
43 43
    container: {
44 -
      flexDirection: 'row',
44 +
      flexDirection: "row",
45 45
      gap: Spacing.inlineGapSm,
46 -
      justifyContent: 'space-around',
47 -
      alignItems: 'flex-start',
48 -
      flexWrap: 'wrap',
46 +
      justifyContent: "space-around",
47 +
      alignItems: "flex-start",
48 +
      flexWrap: "wrap",
49 49
    },
50 50
    itemWrapper: {
51 -
      alignItems: 'center',
51 +
      alignItems: "center",
52 52
      gap: 8,
53 53
      flex: 1,
54 -
      minWidth: '8%',
54 +
      minWidth: "8%",
55 55
    },
56 56
    itemButton: {
57 57
      width: 50,
58 58
      height: 50,
59 59
      borderRadius: BorderRadius.lg,
60 -
      justifyContent: 'center',
61 -
      alignItems: 'center',
60 +
      justifyContent: "center",
61 +
      alignItems: "center",
62 62
      backgroundColor: surfaceColor,
63 63
      borderWidth: 1,
64 64
      borderColor: dividerColor,
65 65
    },
66 66
    itemButtonActive: {
67 67
      backgroundColor: accentBlueColor,
68 -
      borderColor: 'transparent',
68 +
      borderColor: "transparent",
69 69
    },
70 70
    icon: {
71 71
      fontSize: 48,
72 72
      lineHeight: 48,
73 73
    },
75 75
      fontSize: 36,
76 76
      lineHeight: 40,
77 77
    },
78 78
    label: {
79 79
      fontSize: Typography.labelSm.fontSize,
80 -
      fontWeight: '500',
80 +
      fontWeight: "500",
81 81
      color: textSecondaryColor,
82 -
      textAlign: 'center',
82 +
      textAlign: "center",
83 83
    },
84 84
  });
85 85

86 -
  const isEmoji = (icon: IconSource): icon is string => typeof icon === 'string';
86 +
  const isEmoji = (icon: IconSource): icon is string =>
87 +
    typeof icon === "string";
87 88

88 89
  return (
89 90
    <View style={[styles.container, style]}>
90 91
      {items.map((item) => {
91 92
        const isSelected = value === item.id;
120 121
        );
121 122
      })}
122 123
    </View>
123 124
  );
124 125
};