foxygit / FORTIS Log in
commits tags

/fortis/app/(tabs)/index.tsx · 8.32 KB

raw
import { View, Text, ScrollView, TouchableOpacity, StyleSheet } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useRouter } from 'expo-router'
import { Svg, Circle, Path } from 'react-native-svg'
import { Droplets, Zap, Flame } from 'lucide-react-native'
import { useTheme } from '../../src/hooks/useTheme'
import { useProfileStore } from '../../src/store/useProfileStore'
import { useWorkoutStore, calcStreak } from '../../src/store/useWorkoutStore'
import { useNutritionStore, todayFoodLog } from '../../src/store/useNutritionStore'
import { today } from '../../src/lib/macroCalc'

function CalorieRing({ eaten, goal, accent }: { eaten: number; goal: number; accent: string }) {
  const r = 52
  const stroke = 8
  const circ = 2 * Math.PI * r
  const pct = Math.min(eaten / Math.max(goal, 1), 1)
  const dash = circ * pct
  return (
    <Svg width={130} height={130} viewBox="0 0 130 130">
      <Circle cx={65} cy={65} r={r} stroke="rgba(0,0,0,0.07)" strokeWidth={stroke} fill="none" />
      <Circle
        cx={65} cy={65} r={r}
        stroke={accent}
        strokeWidth={stroke}
        fill="none"
        strokeDasharray={`${dash} ${circ}`}
        strokeLinecap="round"
        transform="rotate(-90 65 65)"
      />
    </Svg>
  )
}

export default function HomeScreen() {
  const theme = useTheme()
  const router = useRouter()
  const profile = useProfileStore((s) => s.profile)
  const macroGoals = useProfileStore((s) => s.macroGoals)
  const workouts = useWorkoutStore((s) => s.workouts)
  const workoutLog = useWorkoutStore((s) => s.workoutLog)
  const foodLog = useNutritionStore((s) => s.foodLog)
  const waterToday = useNutritionStore((s) => s.waterToday)
  const waterGoal = useNutritionStore((s) => s.waterGoal)
  const addWater = useNutritionStore((s) => s.addWater)

  const todayLog = todayFoodLog(foodLog)
  const eaten = todayLog.reduce((s, e) => s + e.cal, 0)
  const protEaten = todayLog.reduce((s, e) => s + e.prot, 0)
  const carbEaten = todayLog.reduce((s, e) => s + e.carbs, 0)
  const fatEaten = todayLog.reduce((s, e) => s + e.fat, 0)

  const dow = new Date().getDay() === 0 ? 6 : new Date().getDay() - 1
  const todayWorkout = workouts.find((w) => w.day === dow)
  const streak = calcStreak(workoutLog)

  const s = StyleSheet.create({
    safe: { flex: 1, backgroundColor: theme.outer },
    header: { backgroundColor: theme.header, paddingHorizontal: 22, paddingTop: 12, paddingBottom: 20 },
    greeting: { color: theme.headerText, fontSize: 13, opacity: 0.6, marginBottom: 2 },
    name: { color: theme.headerText, fontSize: 24, fontWeight: '800' },
    streak: { flexDirection: 'row', alignItems: 'center', gap: 4, marginTop: 4 },
    streakText: { color: theme.accent, fontSize: 13, fontWeight: '700' },
    scroll: { flex: 1 },
    card: { backgroundColor: theme.card, borderRadius: 16, margin: 16, marginBottom: 0, padding: 20 },
    calRow: { flexDirection: 'row', alignItems: 'center', gap: 20 },
    calCenter: { alignItems: 'center', position: 'relative' },
    calAbsolute: { position: 'absolute', alignItems: 'center', justifyContent: 'center', top: 0, left: 0, right: 0, bottom: 0 },
    calNum: { color: theme.text, fontSize: 22, fontWeight: '800' },
    calLabel: { color: theme.muted, fontSize: 11 },
    macros: { flex: 1, gap: 10 },
    macroRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
    macroName: { color: theme.muted, fontSize: 12, width: 14 },
    macroTrack: { flex: 1, height: 6, backgroundColor: theme.bg, borderRadius: 3, overflow: 'hidden' },
    macroVal: { color: theme.text, fontSize: 12, fontWeight: '600', width: 40, textAlign: 'right' },
    sectionTitle: { color: theme.text, fontSize: 16, fontWeight: '800', marginBottom: 12 },
    waterRow: { flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' },
    dropWrap: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center' },
    waterBtn: { backgroundColor: theme.accent, borderRadius: 10, paddingHorizontal: 14, paddingVertical: 8, marginLeft: 'auto' },
    waterBtnText: { color: '#FFF', fontSize: 13, fontWeight: '700' },
    waterMl: { color: theme.muted, fontSize: 12, marginTop: 4 },
    workoutCard: { backgroundColor: theme.accent, borderRadius: 16, margin: 16, marginBottom: 0, padding: 20 },
    workoutName: { color: '#FFF', fontSize: 18, fontWeight: '800' },
    workoutSub: { color: 'rgba(255,255,255,0.7)', fontSize: 13, marginTop: 2 },
    startBtn: { backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 10, paddingHorizontal: 16, paddingVertical: 10, marginTop: 14, alignSelf: 'flex-start' },
    startBtnText: { color: '#FFF', fontSize: 14, fontWeight: '700' },
    noWorkout: { color: theme.muted, fontSize: 14 },
    spacer: { height: 24 },
  })

  const drops = Math.round((waterToday / waterGoal) * 8)

  return (
    <SafeAreaView style={s.safe} edges={['top']}>
      <View style={s.header}>
        <Text style={s.greeting}>Hej{profile?.name ? `, ${profile.name}` : ''}! 👋</Text>
        <Text style={s.name}>Dagens översikt</Text>
        {streak > 0 && (
          <View style={s.streak}>
            <Flame size={14} color={theme.accent} />
            <Text style={s.streakText}>{streak} dagars streak</Text>
          </View>
        )}
      </View>

      <ScrollView style={s.scroll} contentContainerStyle={{ paddingBottom: 32 }} showsVerticalScrollIndicator={false}>
        {/* Calorie card */}
        <View style={s.card}>
          <Text style={s.sectionTitle}>Kalorier</Text>
          <View style={s.calRow}>
            <View style={s.calCenter}>
              <CalorieRing eaten={eaten} goal={macroGoals.calories} accent={theme.accent} />
              <View style={s.calAbsolute}>
                <Text style={s.calNum}>{eaten}</Text>
                <Text style={s.calLabel}>/ {macroGoals.calories}</Text>
              </View>
            </View>
            <View style={s.macros}>
              {[
                { label: 'P', val: protEaten, goal: macroGoals.proteinG, color: theme.accent },
                { label: 'K', val: carbEaten, goal: macroGoals.carbsG, color: theme.accent2 },
                { label: 'F', val: fatEaten, goal: macroGoals.fatG, color: '#22A647' },
              ].map((m) => (
                <View key={m.label} style={s.macroRow}>
                  <Text style={s.macroName}>{m.label}</Text>
                  <View style={s.macroTrack}>
                    <View style={{ height: 6, backgroundColor: m.color, borderRadius: 3, width: `${Math.min((m.val / Math.max(m.goal, 1)) * 100, 100)}%` }} />
                  </View>
                  <Text style={s.macroVal}>{m.val}g</Text>
                </View>
              ))}
            </View>
          </View>
        </View>

        {/* Water */}
        <View style={s.card}>
          <Text style={s.sectionTitle}>Vatten</Text>
          <View style={s.waterRow}>
            {Array.from({ length: 8 }).map((_, i) => (
              <View
                key={i}
                style={[s.dropWrap, { backgroundColor: i < drops ? theme.accentLight : theme.bg }]}
              >
                <Droplets size={16} color={i < drops ? theme.accent2 : theme.muted} />
              </View>
            ))}
            <TouchableOpacity style={s.waterBtn} onPress={() => addWater(250)}>
              <Text style={s.waterBtnText}>+ 2,5 dl</Text>
            </TouchableOpacity>
          </View>
          <Text style={s.waterMl}>{waterToday} / {waterGoal} ml</Text>
        </View>

        {/* Today's workout */}
        {todayWorkout ? (
          <TouchableOpacity
            style={s.workoutCard}
            onPress={() => router.push(`/workout/${todayWorkout.id}`)}
          >
            <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
              <Zap size={18} color="#FFF" />
              <Text style={s.workoutName}>{todayWorkout.name}</Text>
            </View>
            <Text style={s.workoutSub}>{todayWorkout.exercises.length} övningar</Text>
            <View style={s.startBtn}>
              <Text style={s.startBtnText}>Starta pass →</Text>
            </View>
          </TouchableOpacity>
        ) : (
          <View style={s.card}>
            <Text style={s.sectionTitle}>Dagens pass</Text>
            <Text style={s.noWorkout}>Ingen träning schemalagd idag — vila!</Text>
          </View>
        )}

        <View style={s.spacer} />
      </ScrollView>
    </SafeAreaView>
  )
}