ci: trigga på master + formatfix inför Gitea Actions
CI / Typecheck, test & build (push) Failing after 2s

This commit is contained in:
Sven (AAMOS AI)
2026-08-13 17:25:14 +07:00
parent 61d60931ad
commit c32a7e33c7
141 changed files with 40555 additions and 45210 deletions
+4 -1
View File
@@ -40,7 +40,10 @@ interface BudgetSummary {
export default function HomeScreen() {
const inventory = useQuery({
queryKey: ["inventory"],
queryFn: () => api<{ items: InventoryItem[]; trustStatus?: "up_to_date" | "needs_check" | "uncertain" }>("/v1/inventory?limit=100"),
queryFn: () =>
api<{ items: InventoryItem[]; trustStatus?: "up_to_date" | "needs_check" | "uncertain" }>(
"/v1/inventory?limit=100",
),
});
const expiring = useQuery({
queryKey: ["inventory-expiring"],
+36 -36
View File
@@ -54,42 +54,42 @@ export default function RootLayout() {
>
<AnalyticsProvider>
<StatusBar style="dark" />
<Stack
screenOptions={{
headerStyle: { backgroundColor: colors.background },
headerTintColor: colors.text,
headerShadowVisible: false,
contentStyle: { backgroundColor: colors.background },
}}
>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/login" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/register" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/forgot-password" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/reset-password" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/verify-email" options={{ headerShown: false }} />
<Stack.Screen name="onboarding" options={{ headerShown: false, gestureEnabled: false }} />
<Stack.Screen name="recipe/[id]" options={{ title: "" }} />
<Stack.Screen
name="cooking/[id]"
options={{ title: "", presentation: "fullScreenModal" }}
/>
<Stack.Screen name="scan-review/[jobId]" options={{ title: t("scan.review.title") }} />
<Stack.Screen name="meal-review/[jobId]" options={{ title: t("scan.meal.title") }} />
<Stack.Screen name="shopping" options={{ title: t("shopping.title") }} />
<Stack.Screen name="meal-boxes" options={{ title: t("mealbox.title") }} />
<Stack.Screen name="household" options={{ title: t("home.household") }} />
<Stack.Screen name="memory" options={{ title: t("memory.title") }} />
<Stack.Screen name="profile" options={{ title: t("profile.title") }} />
<Stack.Screen
name="paywall"
options={{ title: t("paywall.title"), presentation: "modal" }}
/>
<Stack.Screen
name="log-meal"
options={{ title: t("myday.logMeal"), presentation: "modal" }}
/>
<Stack.Screen name="barcode" options={{ title: "Streckkod" }} />
<Stack
screenOptions={{
headerStyle: { backgroundColor: colors.background },
headerTintColor: colors.text,
headerShadowVisible: false,
contentStyle: { backgroundColor: colors.background },
}}
>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/login" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/register" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/forgot-password" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/reset-password" options={{ headerShown: false }} />
<Stack.Screen name="(auth)/verify-email" options={{ headerShown: false }} />
<Stack.Screen name="onboarding" options={{ headerShown: false, gestureEnabled: false }} />
<Stack.Screen name="recipe/[id]" options={{ title: "" }} />
<Stack.Screen
name="cooking/[id]"
options={{ title: "", presentation: "fullScreenModal" }}
/>
<Stack.Screen name="scan-review/[jobId]" options={{ title: t("scan.review.title") }} />
<Stack.Screen name="meal-review/[jobId]" options={{ title: t("scan.meal.title") }} />
<Stack.Screen name="shopping" options={{ title: t("shopping.title") }} />
<Stack.Screen name="meal-boxes" options={{ title: t("mealbox.title") }} />
<Stack.Screen name="household" options={{ title: t("home.household") }} />
<Stack.Screen name="memory" options={{ title: t("memory.title") }} />
<Stack.Screen name="profile" options={{ title: t("profile.title") }} />
<Stack.Screen
name="paywall"
options={{ title: t("paywall.title"), presentation: "modal" }}
/>
<Stack.Screen
name="log-meal"
options={{ title: t("myday.logMeal"), presentation: "modal" }}
/>
<Stack.Screen name="barcode" options={{ title: "Streckkod" }} />
</Stack>
</AnalyticsProvider>
</PersistQueryClientProvider>
+22 -16
View File
@@ -60,10 +60,10 @@ export default function CookingScreen() {
const cook = useMutation({
mutationFn: (body: unknown) =>
api<{ sessionId: string; mealBoxMutations?: Array<{ mealBoxId: string; deltaPortions: number; frozen: boolean }> }>(
`/v1/recipes/${id}/cook`,
{ method: "POST", body },
),
api<{
sessionId: string;
mealBoxMutations?: Array<{ mealBoxId: string; deltaPortions: number; frozen: boolean }>;
}>(`/v1/recipes/${id}/cook`, { method: "POST", body }),
onSuccess: async (data) => {
await queryClient.invalidateQueries({ queryKey: ["inventory"] });
await queryClient.invalidateQueries({ queryKey: ["day"] });
@@ -125,7 +125,9 @@ export default function CookingScreen() {
<Card>
<Heading> {t("cooked.title")}</Heading>
<Body>{recipe.titleSv}</Body>
<Small>{t("cooked.portionsCooked")}: {portionsCooked}</Small>
<Small>
{t("cooked.portionsCooked")}: {portionsCooked}
</Small>
</Card>
<Small>{t("mealbox.guidanceNote")}</Small>
<Button
@@ -230,17 +232,21 @@ export default function CookingScreen() {
})
}
/>
<Button label={t("common.skip")} variant="ghost" onPress={() =>
cook.mutate({
portionsCooked,
mealBoxPortions,
actualPortionsEaten: defaultEaten,
leftoverEstimatePortions: defaultLeftovers,
mealBoxFrozen: false,
deductInventory: true,
mealType: "dinner",
})
} />
<Button
label={t("common.skip")}
variant="ghost"
onPress={() =>
cook.mutate({
portionsCooked,
mealBoxPortions,
actualPortionsEaten: defaultEaten,
leftoverEstimatePortions: defaultLeftovers,
mealBoxFrozen: false,
deductInventory: true,
mealType: "dinner",
})
}
/>
<Button label={t("common.back")} variant="ghost" onPress={() => setFinishing(false)} />
</Screen>
);
+9 -14
View File
@@ -75,8 +75,7 @@ export default function MealBoxesScreen() {
{boxes.length === 0 && <EmptyState text={t("mealbox.empty")} />}
{boxes.map((box) => {
const urgent = box.recommendedUseBy <= today;
const canUndo =
!!box.cookingSessionId && new Date(box.createdAt).getTime() > cutoff;
const canUndo = !!box.cookingSessionId && new Date(box.createdAt).getTime() > cutoff;
return (
<Card key={box.id}>
<Row style={{ justifyContent: "space-between" }}>
@@ -109,18 +108,14 @@ export default function MealBoxesScreen() {
onPress={() => {
const sessionId = box.cookingSessionId;
if (!sessionId) return;
Alert.alert(
t("mealbox.undoConfirmTitle"),
t("mealbox.undoConfirmBody"),
[
{ text: t("common.cancel"), style: "cancel" },
{
text: t("common.undo"),
style: "destructive",
onPress: () => undo.mutate(sessionId),
},
],
);
Alert.alert(t("mealbox.undoConfirmTitle"), t("mealbox.undoConfirmBody"), [
{ text: t("common.cancel"), style: "cancel" },
{
text: t("common.undo"),
style: "destructive",
onPress: () => undo.mutate(sessionId),
},
]);
}}
/>
</Row>
+25 -5
View File
@@ -5,8 +5,19 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { api } from "@/lib/api";
import { t } from "@/lib/i18n";
import {
Body, Button, Card, ErrorView, Heading, Input,
LoadingView, Row, Screen, Small, Spacer, Tag, Title,
Body,
Button,
Card,
ErrorView,
Heading,
Input,
LoadingView,
Row,
Screen,
Small,
Spacer,
Tag,
Title,
} from "@/components/ui";
import { spacing } from "@/lib/theme";
@@ -17,9 +28,16 @@ import { spacing } from "@/lib/theme";
*/
const MEAL_TYPES = ["breakfast", "lunch", "dinner", "snack"] as const;
interface MealComponent { name: string; estimatedGrams: number | null; confidence: number }
interface MealComponent {
name: string;
estimatedGrams: number | null;
confidence: number;
}
interface ScanJob {
id: string; status: string; scanType: string; error: string | null;
id: string;
status: string;
scanType: string;
error: string | null;
result: {
kcalRange?: { min: number; max: number; mostLikely: number } | null;
components?: MealComponent[];
@@ -97,7 +115,9 @@ export default function MealReviewScreen() {
{kcal ? (
<>
<Title> {Math.round(kcal.mostLikely)} kcal</Title>
<Small>{Math.round(kcal.min)}{Math.round(kcal.max)} kcal</Small>
<Small>
{Math.round(kcal.min)}{Math.round(kcal.max)} kcal
</Small>
</>
) : (
<Body>{t("scan.meal.noEstimate")}</Body>
+6 -6
View File
@@ -5,11 +5,7 @@ import { api } from "@/lib/api";
import { useAuth } from "@/lib/auth";
import { useAnalytics } from "@/lib/analytics";
import { t } from "@/lib/i18n";
import {
onboardingStarted,
onboardingStepCompleted,
onboardingSkipped,
} from "@app/analytics";
import { onboardingStarted, onboardingStepCompleted, onboardingSkipped } from "@app/analytics";
import {
Body,
Button,
@@ -113,7 +109,11 @@ export default function OnboardingScreen() {
precisionMode: mode,
},
});
track(onboardingStepCompleted({ properties: { step: "a", goals, primaryGoal, precisionMode: mode } }));
track(
onboardingStepCompleted({
properties: { step: "a", goals, primaryGoal, precisionMode: mode },
}),
);
setOnboardingStep(res.step);
setLayer("b");
// Let user into the app Step B will be shown contextually later
+5 -1
View File
@@ -53,7 +53,11 @@ export default function PaywallScreen() {
});
useEffect(() => {
track(paywallViewed({ properties: { plan: entitlements.data?.plan, status: entitlements.data?.status } }));
track(
paywallViewed({
properties: { plan: entitlements.data?.plan, status: entitlements.data?.status },
}),
);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const trialDaysLeft =
+8 -5
View File
@@ -47,11 +47,16 @@ export default function ReconciliationScreen() {
const query = useQuery({
queryKey: ["reconciliation-candidates"],
queryFn: () => api<{ candidates: Candidate[] }>("/v1/reconciliations/start", { method: "POST", body: {} }),
queryFn: () =>
api<{ candidates: Candidate[] }>("/v1/reconciliations/start", { method: "POST", body: {} }),
});
const resolveMutation = useMutation({
mutationFn: (input: { itemId: string; action: Candidate["suggestedAction"]; quantity?: number }) =>
mutationFn: (input: {
itemId: string;
action: Candidate["suggestedAction"];
quantity?: number;
}) =>
api<{ itemId: string; action: string; quantity: number; verifiedByUser: boolean }>(
`/v1/reconciliations/items/${input.itemId}/resolve`,
{ method: "POST", body: { action: input.action, quantity: input.quantity } },
@@ -100,9 +105,7 @@ export default function ReconciliationScreen() {
<Card>
<Row style={{ justifyContent: "space-between" }}>
<Heading>{candidate!.displayName}</Heading>
<Body>
{formatQuantity(candidate!.quantity, candidate!.unit)}
</Body>
<Body>{formatQuantity(candidate!.quantity, candidate!.unit)}</Body>
</Row>
<Small>{candidate!.locationName}</Small>
<Spacer size={spacing.xs} />
@@ -39,7 +39,8 @@ export default function ScanDiffReviewScreen() {
const query = useQuery({
queryKey: ["scan-diff", jobId],
queryFn: () => api<{ rows: DiffRow[] }>(`/v1/scans/${jobId}/diff`, { method: "POST", body: {} }),
queryFn: () =>
api<{ rows: DiffRow[] }>(`/v1/scans/${jobId}/diff`, { method: "POST", body: {} }),
});
const apply = useMutation({
+2 -11
View File
@@ -1,11 +1,5 @@
import { useState } from "react";
import {
Linking,
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
import { Linking, Pressable, StyleSheet, Text, View } from "react-native";
import { Body, Button, Screen, Small, Title } from "@/components/ui";
import type { TextStyle } from "react-native";
import { acceptConsent } from "@/lib/api";
@@ -55,10 +49,7 @@ export function ConsentScreen({ onAccepted }: ConsentScreenProps) {
</View>
<View style={{ gap: spacing.md, paddingBottom: spacing.lg }}>
<Pressable
onPress={() => setChecked((v) => !v)}
style={styles.checkboxRow}
>
<Pressable onPress={() => setChecked((v) => !v)} style={styles.checkboxRow}>
<View style={[styles.box, checked && styles.boxChecked]}>
{checked && <Text style={styles.checkmark}></Text>}
</View>
@@ -23,7 +23,13 @@ export function useFirstScanCoach() {
return { visible, showIfNeeded, dismiss };
}
export function FirstScanCoach({ visible, onDismiss }: { visible: boolean; onDismiss: () => void }) {
export function FirstScanCoach({
visible,
onDismiss,
}: {
visible: boolean;
onDismiss: () => void;
}) {
return (
<Modal visible={visible} transparent animationType="fade">
<View
+1 -6
View File
@@ -2,12 +2,7 @@ import { createContext, useContext, useEffect, useMemo, useRef, useState } from
import { Platform } from "react-native";
import Constants from "expo-constants";
import AsyncStorage from "@react-native-async-storage/async-storage";
import {
type AnalyticsEvent,
type Tracker,
createHttpSend,
createTracker,
} from "@app/analytics";
import { type AnalyticsEvent, type Tracker, createHttpSend, createTracker } from "@app/analytics";
import { API_BASE } from "./api";
import { useAuth } from "./auth";
import { BRAND } from "./brand";
+3 -10
View File
@@ -4,16 +4,9 @@
"strict": true,
"noUncheckedIndexedAccess": true,
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}