eletrotupi / tcc/ frontend/components/medicine/TodayMedicines.tsxc901c35af9c84b6a29e5185545ced1129f33b8f4
4.9 KB | 151 lines Raw
1
import { View, TouchableOpacity, StyleSheet, ActivityIndicator } from "react-native";
2
import { Text } from "@/components/ui/Text";
3
import { Ionicons } from "@expo/vector-icons";
4
import { Colors, Typography, Spacing, BorderRadius, Shadows } from "@/constants/theme";
5
import { useMedicineToday, useCreateCareAction, medicineTodayKeys } from "@/hooks";
6
import { useQueryClient } from "@tanstack/react-query";
7
import { TodayMedicineEntry } from "@/lib/api";
8

9
function sortedTimes(scheduledAt: string[]): string[] {
10
  return [...scheduledAt].sort((a, b) => {
11
    const [ah, am] = a.split(":").map(Number);
12
    const [bh, bm] = b.split(":").map(Number);
13
    return ah * 60 + am - (bh * 60 + bm);
14
  });
15
}
16

17
function confirmedIndexes(entry: TodayMedicineEntry): Set<number> {
18
  const confirmed = new Set<number>();
19
  const n = Math.min(entry.logs.length, entry.regimen.scheduledAt.length);
20
  for (let i = 0; i < n; i++) confirmed.add(i);
21
  return confirmed;
22
}
23

24
export function TodayMedicines() {
25
  const { data, isLoading } = useMedicineToday();
26
  const createCareAction = useCreateCareAction();
27
  const queryClient = useQueryClient();
28

29
  if (isLoading) {
30
    return (
31
      <View style={styles.center}>
32
        <ActivityIndicator color={Colors.light.tint} />
33
      </View>
34
    );
35
  }
36

37
  const entries = data?.regimens ?? [];
38

39
  if (entries.length === 0) {
40
    return (
41
      <View style={styles.empty}>
42
        <Text style={styles.emptyText}>Nenhum medicamento agendado para hoje</Text>
43
      </View>
44
    );
45
  }
46

47
  console.log("today: ", JSON.stringify(entries))
48

49
  const handleConfirm = async (regimenId: number) => {
50
    await createCareAction.mutateAsync({
51
      type: "MEDICINE",
52
      moment: new Date(),
53
      regimenId,
54
    });
55

56
    queryClient.invalidateQueries({ queryKey: medicineTodayKeys.list() });
57
  };
58

59
  return (
60
    <View style={styles.container}>
61
      {entries.map((entry) => {
62
        const times = entry.regimen.scheduledAt.length > 0
63
          ? sortedTimes(entry.regimen.scheduledAt)
64
          : ["hoje"];
65
        const confirmed = confirmedIndexes(entry);
66

67
        return (
68
          <View key={entry.regimen.id} style={styles.card}>
69
            <View style={styles.cardHeader}>
70
              <View style={styles.iconWrap}>
71
                <Ionicons name="bandage-outline" size={18} color={Colors.light.tint} />
72
              </View>
73
              <View style={styles.cardInfo}>
74
                <Text style={styles.name}>{entry.regimen.name}</Text>
75
                <Text style={styles.dosage}>{entry.regimen.dosage}</Text>
76
              </View>
77
            </View>
78

79
            {times.map((time, idx) => {
80
              const isDone = time === "hoje" ? entry.logs.length > 0 : confirmed.has(idx);
81

82
              return (
83
                <TouchableOpacity
84
                  key={time}
85
                  style={[styles.timeRow, isDone && styles.timeRowConfirmed]}
86
                  onPress={isDone ? undefined : () => handleConfirm(entry.regimen.id)}
87
                  activeOpacity={isDone ? 1 : 0.7}
88
                  disabled={isDone || createCareAction.isPending}
89
                >
90
                  <Text style={[styles.timeText, isDone && styles.timeTextConfirmed]}>
91
                    {time === "hoje" ? "Hoje" : time}
92
                  </Text>
93
                  {isDone ? (
94
                    <Ionicons name="checkmark-circle" size={20} color="#34C759" />
95
                  ) : (
96
                    <View style={styles.pendingCircle} />
97
                  )}
98
                </TouchableOpacity>
99
              );
100
            })}
101
          </View>
102
        );
103
      })}
104
    </View>
105
  );
106
}
107

108
const styles = StyleSheet.create({
109
  container: { gap: Spacing.cardGap },
110
  center: { padding: Spacing.cardGap, alignItems: "center" },
111
  empty: { padding: Spacing.cardGap },
112
  emptyText: { ...Typography.bodyMd, color: Colors.light.textSecondary },
113
  card: {
114
    backgroundColor: "#fff",
115
    borderRadius: BorderRadius.lg,
116
    padding: Spacing.cardGap,
117
    gap: 10,
118
    ...Shadows.sm,
119
  },
120
  cardHeader: { flexDirection: "row", alignItems: "center", gap: 10 },
121
  iconWrap: {
122
    width: 32,
123
    height: 32,
124
    borderRadius: BorderRadius.md,
125
    backgroundColor: Colors.light.tint + "1A",
126
    alignItems: "center",
127
    justifyContent: "center",
128
  },
129
  cardInfo: { flex: 1 },
130
  name: { ...Typography.bodyMd, fontWeight: "600" },
131
  dosage: { ...Typography.labelSm, color: Colors.light.textSecondary },
132
  timeRow: {
133
    flexDirection: "row",
134
    alignItems: "center",
135
    justifyContent: "space-between",
136
    paddingVertical: 8,
137
    paddingHorizontal: 12,
138
    borderRadius: BorderRadius.md,
139
    backgroundColor: Colors.light.background,
140
  },
141
  timeRowConfirmed: { backgroundColor: "#F0FFF4", opacity: 0.8 },
142
  timeText: { ...Typography.bodyMd, fontWeight: "500" },
143
  timeTextConfirmed: { color: Colors.light.textSecondary },
144
  pendingCircle: {
145
    width: 20,
146
    height: 20,
147
    borderRadius: 10,
148
    borderWidth: 2,
149
    borderColor: Colors.light.divider,
150
  },
151
});