feat(mobile): redigera kost/allergier/religion i profilen när som helst (MIKRO 63)
CI / Typecheck, test & build (push) Failing after 43s
CI / Typecheck, test & build (push) Failing after 43s
This commit is contained in:
@@ -37,6 +37,11 @@ interface Consent {
|
||||
kind: string;
|
||||
status: string;
|
||||
}
|
||||
interface Prefs {
|
||||
dietPattern?: string | null;
|
||||
allergens?: string[] | null;
|
||||
religiousRule?: string | null;
|
||||
}
|
||||
|
||||
/** Samtyckesetiketter ur i18n-katalogen: profile.consent.<kind> (12 språk). */
|
||||
const CONSENT_KINDS = [
|
||||
@@ -48,6 +53,9 @@ const CONSENT_KINDS = [
|
||||
"push_notifications",
|
||||
"product_analytics",
|
||||
] as const;
|
||||
const DIETS = ["omnivore", "flexitarian", "pescatarian", "vegetarian", "vegan"] as const;
|
||||
const ALLERGENS = ["gluten", "milk", "eggs", "tree_nuts", "peanuts", "fish", "crustaceans", "soy", "sesame"] as const;
|
||||
const RELIGIOUS = ["none", "halal", "kosher", "hindu_no_beef", "buddhist_vegetarian"] as const;
|
||||
|
||||
export default function ProfileScreen() {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -62,6 +70,17 @@ export default function ProfileScreen() {
|
||||
queryKey: ["consents"],
|
||||
queryFn: () => api<Consent[]>("/v1/me/consents"),
|
||||
});
|
||||
const prefs = useQuery({
|
||||
queryKey: ["preferences"],
|
||||
queryFn: () => api<Prefs | null>("/v1/me/preferences"),
|
||||
});
|
||||
const savePrefs = useMutation({
|
||||
mutationFn: (body: Record<string, unknown>) =>
|
||||
api("/v1/me/preferences", { method: "PATCH", body }),
|
||||
onSuccess: () => void queryClient.invalidateQueries({ queryKey: ["preferences"] }),
|
||||
onError: (err) =>
|
||||
Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")),
|
||||
});
|
||||
|
||||
const setConsent = useMutation({
|
||||
mutationFn: ({ kind, granted }: { kind: string; granted: boolean }) =>
|
||||
@@ -111,6 +130,9 @@ export default function ProfileScreen() {
|
||||
if (me.isError) return <ErrorView onRetry={() => void me.refetch()} />;
|
||||
|
||||
const consentMap = new Map((consents.data ?? []).map((c) => [c.kind, c.status]));
|
||||
const curDiet = prefs.data?.dietPattern ?? "omnivore";
|
||||
const curAllergens = prefs.data?.allergens ?? [];
|
||||
const curReligious = prefs.data?.religiousRule ?? "none";
|
||||
|
||||
return (
|
||||
<Screen>
|
||||
@@ -162,6 +184,50 @@ export default function ProfileScreen() {
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Heading>{t("onboarding.diet")}</Heading>
|
||||
<Row style={{ flexWrap: "wrap" }}>
|
||||
{DIETS.map((id) => (
|
||||
<Button
|
||||
key={id}
|
||||
label={t(`onboarding.diet.${id}` as never)}
|
||||
variant={curDiet === id ? "secondary" : "ghost"}
|
||||
onPress={() => savePrefs.mutate({ dietPattern: id })}
|
||||
/>
|
||||
))}
|
||||
</Row>
|
||||
<Spacer size={8} />
|
||||
<Heading>{t("onboarding.allergies")}</Heading>
|
||||
<Row style={{ flexWrap: "wrap" }}>
|
||||
{ALLERGENS.map((id) => (
|
||||
<Button
|
||||
key={id}
|
||||
label={t(`onboarding.allergen.${id}` as never)}
|
||||
variant={curAllergens.includes(id) ? "secondary" : "ghost"}
|
||||
onPress={() =>
|
||||
savePrefs.mutate({
|
||||
allergens: curAllergens.includes(id)
|
||||
? curAllergens.filter((a) => a !== id)
|
||||
: [...curAllergens, id],
|
||||
})
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Row>
|
||||
<Spacer size={8} />
|
||||
<Heading>{t("onboarding.religious")}</Heading>
|
||||
<Row style={{ flexWrap: "wrap" }}>
|
||||
{RELIGIOUS.map((id) => (
|
||||
<Button
|
||||
key={id}
|
||||
label={t(`onboarding.religious.${id}` as never)}
|
||||
variant={curReligious === id ? "secondary" : "ghost"}
|
||||
onPress={() => savePrefs.mutate({ religiousRule: id })}
|
||||
/>
|
||||
))}
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
{entitlements.data && (
|
||||
<Card onPress={() => router.push("/paywall")}>
|
||||
<Row style={{ justifyContent: "space-between" }}>
|
||||
|
||||
Reference in New Issue
Block a user