222 lines
7.8 KiB
TypeScript
222 lines
7.8 KiB
TypeScript
import React, { useCallback, useEffect, useState } from "react";
|
|
import {
|
|
View,
|
|
ScrollView,
|
|
Switch,
|
|
ActivityIndicator,
|
|
TextInput,
|
|
useColorScheme,
|
|
Pressable,
|
|
} from "react-native";
|
|
import { useSirouRouter } from "@sirou/react-native";
|
|
import { AppRoutes } from "@/lib/routes";
|
|
import { Text } from "@/components/ui/text";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
|
import { StandardHeader } from "@/components/StandardHeader";
|
|
import { api } from "@/lib/api";
|
|
import { Bell, CalendarSearch, FileText, Newspaper, ChevronRight } from "@/lib/icons";
|
|
import { getPlaceholderColor } from "@/lib/colors";
|
|
import { PickerModal, SelectOption } from "@/components/PickerModal";
|
|
|
|
type NotificationSettings = {
|
|
id: string;
|
|
invoiceReminders: boolean;
|
|
daysBeforeDueDate: number;
|
|
newsAlerts: boolean;
|
|
reportReady: boolean;
|
|
userId: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export default function NotificationSettingsScreen() {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const colorScheme = useColorScheme();
|
|
const isDark = colorScheme === "dark";
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [settings, setSettings] = useState<NotificationSettings | null>(null);
|
|
|
|
const [invoiceReminders, setInvoiceReminders] = useState(false);
|
|
const [daysBeforeDueDate, setDaysBeforeDueDate] = useState("0");
|
|
const [newsAlerts, setNewsAlerts] = useState(false);
|
|
const [reportReady, setReportReady] = useState(false);
|
|
const [daysModalVisible, setDaysModalVisible] = useState(false);
|
|
|
|
const daysOptions = [
|
|
{ label: "1 day", value: "1" },
|
|
{ label: "3 days", value: "3" },
|
|
{ label: "7 days", value: "7" },
|
|
{ label: "14 days", value: "14" },
|
|
{ label: "30 days", value: "30" },
|
|
];
|
|
|
|
const loadSettings = useCallback(async () => {
|
|
try {
|
|
setLoading(true);
|
|
const res = await (api as any).notifications.settings();
|
|
const data = res?.data ?? res;
|
|
setSettings(data);
|
|
setInvoiceReminders(Boolean(data?.invoiceReminders));
|
|
setNewsAlerts(Boolean(data?.newsAlerts));
|
|
setReportReady(Boolean(data?.reportReady));
|
|
setDaysBeforeDueDate(String(data?.daysBeforeDueDate ?? 0));
|
|
} catch (e) {
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
loadSettings();
|
|
}, [loadSettings]);
|
|
|
|
const onSave = async () => {
|
|
setSaving(true);
|
|
try {
|
|
await api.notifications.update({
|
|
body: {
|
|
invoiceReminders,
|
|
daysBeforeDueDate: parseInt(daysBeforeDueDate),
|
|
newsAlerts,
|
|
reportReady,
|
|
},
|
|
});
|
|
nav.back();
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<StandardHeader showBack title="Notification settings" />
|
|
|
|
{loading ? (
|
|
<View className="flex-1 items-center justify-center">
|
|
<ActivityIndicator />
|
|
</View>
|
|
) : (
|
|
<ScrollView
|
|
contentContainerStyle={{ padding: 16, paddingBottom: 110 }}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<View className="mb-5">
|
|
<Text variant="muted" className="text-xs font-semibold mb-2 px-1">
|
|
Preferences
|
|
</Text>
|
|
|
|
<Card className="overflow-hidden">
|
|
<CardContent className="p-0">
|
|
<View className="flex-row items-center px-4 py-3 border-b border-border/40">
|
|
<View className="h-9 w-9 rounded-[8px] items-center justify-center mr-3">
|
|
<Bell size={17} color="#ea580c" />
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text className="text-foreground font-medium">
|
|
Invoice reminders
|
|
</Text>
|
|
<Text variant="muted" className="text-xs mt-0.5">
|
|
Get reminders before invoices are due
|
|
</Text>
|
|
</View>
|
|
<Switch
|
|
value={invoiceReminders}
|
|
onValueChange={setInvoiceReminders}
|
|
trackColor={{ false: "#94a3b8", true: "#ea580c" }}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
|
|
<View className="px-4 py-3 border-b border-border/40">
|
|
<View className="flex-row items-center mb-2">
|
|
<View className="h-9 w-9 rounded-[8px] items-center justify-center mr-3">
|
|
<CalendarSearch size={17} color="#ea580c" />
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text className="text-foreground font-medium">
|
|
Days before due date
|
|
</Text>
|
|
<Text variant="muted" className="text-xs mt-0.5">
|
|
Currently: {daysBeforeDueDate} days
|
|
</Text>
|
|
</View>
|
|
<Pressable onPress={() => setDaysModalVisible(true)}>
|
|
<ChevronRight size={18} color="#ea580c" />
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
|
|
<View className="flex-row items-center px-4 py-3 border-b border-border/40">
|
|
<View className="h-9 w-9 rounded-[8px] items-center justify-center mr-3">
|
|
<Newspaper size={17} color="#ea580c" />
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text className="text-foreground font-medium">News alerts</Text>
|
|
<Text variant="muted" className="text-xs mt-0.5">
|
|
Product updates and announcements
|
|
</Text>
|
|
</View>
|
|
<Switch
|
|
value={newsAlerts}
|
|
onValueChange={setNewsAlerts}
|
|
trackColor={{ false: "#94a3b8", true: "#ea580c" }}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
|
|
<View className="flex-row items-center px-4 py-3">
|
|
<View className="h-9 w-9 rounded-[8px] items-center justify-center mr-3">
|
|
<FileText size={17} color="#ea580c" />
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text className="text-foreground font-medium">Report ready</Text>
|
|
<Text variant="muted" className="text-xs mt-0.5">
|
|
Notify when reports are generated
|
|
</Text>
|
|
</View>
|
|
<Switch
|
|
value={reportReady}
|
|
onValueChange={setReportReady}
|
|
trackColor={{ false: "#94a3b8", true: "#ea580c" }}
|
|
thumbColor="#ffffff"
|
|
/>
|
|
</View>
|
|
</CardContent>
|
|
</Card>
|
|
</View>
|
|
</ScrollView>
|
|
)}
|
|
|
|
<View className="absolute bottom-0 pb-10 left-0 right-0 p-4 bg-background border-t border-border">
|
|
<Button className="bg-primary" onPress={onSave} disabled={saving || loading}>
|
|
<Text className="text-white font-semibold">
|
|
{saving ? "Saving..." : "Save"}
|
|
</Text>
|
|
</Button>
|
|
</View>
|
|
|
|
<PickerModal
|
|
visible={daysModalVisible}
|
|
title="Select Days"
|
|
onClose={() => setDaysModalVisible(false)}
|
|
>
|
|
{daysOptions.map((option) => (
|
|
<SelectOption
|
|
key={option.value}
|
|
label={option.label}
|
|
value={option.value}
|
|
selected={option.value === daysBeforeDueDate}
|
|
onSelect={(value: string) => {
|
|
setDaysBeforeDueDate(value);
|
|
setDaysModalVisible(false);
|
|
}}
|
|
/>
|
|
))}
|
|
</PickerModal>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|