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;
|
kind: string;
|
||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
|
interface Prefs {
|
||||||
|
dietPattern?: string | null;
|
||||||
|
allergens?: string[] | null;
|
||||||
|
religiousRule?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Samtyckesetiketter ur i18n-katalogen: profile.consent.<kind> (12 språk). */
|
/** Samtyckesetiketter ur i18n-katalogen: profile.consent.<kind> (12 språk). */
|
||||||
const CONSENT_KINDS = [
|
const CONSENT_KINDS = [
|
||||||
@@ -48,6 +53,9 @@ const CONSENT_KINDS = [
|
|||||||
"push_notifications",
|
"push_notifications",
|
||||||
"product_analytics",
|
"product_analytics",
|
||||||
] as const;
|
] 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() {
|
export default function ProfileScreen() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -62,6 +70,17 @@ export default function ProfileScreen() {
|
|||||||
queryKey: ["consents"],
|
queryKey: ["consents"],
|
||||||
queryFn: () => api<Consent[]>("/v1/me/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({
|
const setConsent = useMutation({
|
||||||
mutationFn: ({ kind, granted }: { kind: string; granted: boolean }) =>
|
mutationFn: ({ kind, granted }: { kind: string; granted: boolean }) =>
|
||||||
@@ -111,6 +130,9 @@ export default function ProfileScreen() {
|
|||||||
if (me.isError) return <ErrorView onRetry={() => void me.refetch()} />;
|
if (me.isError) return <ErrorView onRetry={() => void me.refetch()} />;
|
||||||
|
|
||||||
const consentMap = new Map((consents.data ?? []).map((c) => [c.kind, c.status]));
|
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 (
|
return (
|
||||||
<Screen>
|
<Screen>
|
||||||
@@ -162,6 +184,50 @@ export default function ProfileScreen() {
|
|||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</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 && (
|
{entitlements.data && (
|
||||||
<Card onPress={() => router.push("/paywall")}>
|
<Card onPress={() => router.push("/paywall")}>
|
||||||
<Row style={{ justifyContent: "space-between" }}>
|
<Row style={{ justifyContent: "space-between" }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user