284 lines
8.3 KiB
TypeScript
284 lines
8.3 KiB
TypeScript
import React, { useState } from "react";
|
|
import { View, ScrollView, Pressable, Image } from "react-native";
|
|
import { useSirouRouter } from "@sirou/react-native";
|
|
import { AppRoutes } from "@/lib/routes";
|
|
import { Text } from "@/components/ui/text";
|
|
import {
|
|
ArrowLeft,
|
|
Settings,
|
|
ChevronRight,
|
|
HelpCircle,
|
|
History,
|
|
LogOut,
|
|
User,
|
|
Lock,
|
|
Globe,
|
|
Building2,
|
|
Users,
|
|
} from "@/lib/icons";
|
|
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
|
import { useColorScheme } from "nativewind";
|
|
import { saveTheme, AppTheme } from "@/lib/theme";
|
|
import { useAuthStore } from "@/lib/auth-store";
|
|
import { usePinStore } from "@/lib/pin-store";
|
|
import { useLanguageStore, AppLanguage } from "@/lib/language-store";
|
|
import { LanguageModal } from "@/components/LanguageModal";
|
|
import { ThemeModal } from "@/components/ThemeModal";
|
|
|
|
const AVATAR_FALLBACK_BASE =
|
|
"https://ui-avatars.com/api/?background=ea580c&color=fff&name=";
|
|
|
|
const THEME_OPTIONS = [
|
|
{ value: "light", label: "Light" },
|
|
{ value: "dark", label: "Dark" },
|
|
{ value: "system", label: "System Default" },
|
|
] as const;
|
|
|
|
type ThemeOption = (typeof THEME_OPTIONS)[number]["value"];
|
|
|
|
const LANGUAGE_OPTIONS = [
|
|
{ value: "en", label: "English" },
|
|
{ value: "am", label: "Amharic" },
|
|
] as const;
|
|
|
|
type LanguageOption = (typeof LANGUAGE_OPTIONS)[number]["value"];
|
|
|
|
function MenuGroup({
|
|
label,
|
|
children,
|
|
}: {
|
|
label: string;
|
|
children: React.ReactNode;
|
|
}) {
|
|
return (
|
|
<View className="mb-5">
|
|
<Text className="text-[16px] font-sans-bold text-foreground mb-2 px-1">
|
|
{label}
|
|
</Text>
|
|
<View className="rounded-[10px] border border-border bg-card overflow-hidden">
|
|
{children}
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function MenuItem({
|
|
icon,
|
|
label,
|
|
sublabel,
|
|
onPress,
|
|
right,
|
|
destructive = false,
|
|
isLast = false,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
label: string;
|
|
sublabel?: string;
|
|
onPress?: () => void;
|
|
right?: React.ReactNode;
|
|
destructive?: boolean;
|
|
isLast?: boolean;
|
|
}) {
|
|
return (
|
|
<Pressable
|
|
onPress={onPress}
|
|
className={`flex-row items-center px-4 py-3 ${
|
|
!isLast ? "border-b border-border/40" : ""
|
|
}`}
|
|
>
|
|
<View className="h-9 w-9 rounded-[8px] items-center justify-center mr-3">
|
|
{icon}
|
|
</View>
|
|
<View className="flex-1">
|
|
<Text
|
|
className={`text-sm font-sans-bold ${destructive ? "text-red-500" : "text-foreground"}`}
|
|
>
|
|
{label}
|
|
</Text>
|
|
{sublabel ? (
|
|
<Text className="text-[11px] font-sans-bold text-muted-foreground mt-0.5">
|
|
{sublabel}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
{right !== undefined ? (
|
|
right
|
|
) : (
|
|
<ChevronRight color={destructive ? "#ef4444" : "#94a3b8"} size={18} />
|
|
)}
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
export default function ProfileScreen() {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const { user, logout } = useAuthStore();
|
|
const { setColorScheme, colorScheme } = useColorScheme();
|
|
const { language, setLanguage } = useLanguageStore();
|
|
const [themeSheetVisible, setThemeSheetVisible] = useState(false);
|
|
const [languageSheetVisible, setLanguageSheetVisible] = useState(false);
|
|
|
|
const currentTheme: ThemeOption = (colorScheme as ThemeOption) ?? "system";
|
|
const currentLanguage: LanguageOption = (language as LanguageOption) ?? "en";
|
|
|
|
const handleThemeSelect = (val: AppTheme) => {
|
|
setColorScheme(val);
|
|
saveTheme(val);
|
|
};
|
|
|
|
const iconColor = colorScheme === "dark" ? "#f8fafc" : "#0f172a";
|
|
|
|
return (
|
|
<ScreenWrapper className="bg-background">
|
|
<View className="px-6 pt-4 flex-row justify-between items-center">
|
|
<Pressable
|
|
onPress={() => nav.back()}
|
|
className="h-10 w-10 rounded-[10px] bg-card items-center justify-center border border-border"
|
|
>
|
|
<ArrowLeft color={iconColor} size={20} />
|
|
</Pressable>
|
|
<Text className="text-foreground text-[17px] font-sans-bold tracking-tight">
|
|
Profile
|
|
</Text>
|
|
<Pressable
|
|
onPress={() => nav.go("edit-profile")}
|
|
className="h-10 w-10 rounded-[10px] bg-card items-center justify-center border border-border"
|
|
>
|
|
<User color={iconColor} size={18} />
|
|
</Pressable>
|
|
</View>
|
|
|
|
<ScrollView
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{
|
|
paddingHorizontal: 16,
|
|
paddingTop: 24,
|
|
paddingBottom: 80,
|
|
}}
|
|
>
|
|
<View className="items-center mb-8">
|
|
<View className="h-20 w-20 rounded-full overflow-hidden bg-muted mb-3">
|
|
<Image
|
|
source={{
|
|
uri:
|
|
user?.avatar ||
|
|
`${AVATAR_FALLBACK_BASE}${encodeURIComponent(`${user?.firstName} ${user?.lastName}`)}`,
|
|
}}
|
|
className="h-full w-full"
|
|
/>
|
|
</View>
|
|
<Text className="text-foreground text-lg font-sans-bold tracking-tight">
|
|
{user?.firstName} {user?.lastName}
|
|
</Text>
|
|
<Text className="text-sm font-sans-bold text-foreground mt-0.5">
|
|
{user?.email}
|
|
</Text>
|
|
</View>
|
|
|
|
<MenuGroup label="Preferences">
|
|
<MenuItem
|
|
icon={<Settings color={iconColor} size={17} />}
|
|
label="Appearance"
|
|
sublabel={
|
|
THEME_OPTIONS.find((o) => o.value === currentTheme)?.label ??
|
|
"System Default"
|
|
}
|
|
onPress={() => setThemeSheetVisible(true)}
|
|
/>
|
|
<MenuItem
|
|
icon={<Globe color={iconColor} size={17} />}
|
|
label="Language"
|
|
sublabel={
|
|
LANGUAGE_OPTIONS.find((o) => o.value === currentLanguage)
|
|
?.label ?? "English"
|
|
}
|
|
onPress={() => setLanguageSheetVisible(true)}
|
|
/>
|
|
<MenuItem
|
|
icon={<Lock color={iconColor} size={17} />}
|
|
label="Password"
|
|
sublabel="Change your password"
|
|
onPress={() => nav.go("change-pin")}
|
|
/>
|
|
{/* <MenuItem
|
|
icon={<Lock color={iconColor} size={17} />}
|
|
label="Lock Now"
|
|
sublabel="Lock screen immediately"
|
|
onPress={() => {
|
|
usePinStore.getState().lock();
|
|
nav.go("pin-lock");
|
|
}}
|
|
isLast
|
|
/> */}
|
|
</MenuGroup>
|
|
|
|
<MenuGroup label="Company">
|
|
<MenuItem
|
|
icon={<Building2 color={iconColor} size={17} />}
|
|
label="Overview"
|
|
sublabel="View company details"
|
|
onPress={() => nav.go("company-details")}
|
|
/>
|
|
{/* <MenuItem
|
|
icon={<Building2 color={iconColor} size={17} />}
|
|
label="Edit Company Info"
|
|
sublabel="Update business details"
|
|
onPress={() => nav.go("company/edit")}
|
|
/> */}
|
|
<MenuItem
|
|
icon={<Users color={iconColor} size={17} />}
|
|
label="Workers"
|
|
sublabel="Manage team members"
|
|
onPress={() => nav.go("team/index")}
|
|
isLast
|
|
/>
|
|
</MenuGroup>
|
|
|
|
<MenuGroup label="Support & Legal">
|
|
<MenuItem
|
|
icon={<HelpCircle color={iconColor} size={17} />}
|
|
label="Help & Support"
|
|
sublabel="Create support tickets"
|
|
onPress={() => nav.go("help")}
|
|
/>
|
|
<MenuItem
|
|
icon={<Globe color={iconColor} size={17} />}
|
|
label="FAQ"
|
|
sublabel="Quick Answers"
|
|
onPress={() => nav.go("faq")}
|
|
isLast
|
|
/>
|
|
</MenuGroup>
|
|
|
|
<View className="rounded-[10px] border border-border bg-card overflow-hidden">
|
|
<MenuItem
|
|
icon={<LogOut color="#ef4444" size={17} />}
|
|
label="Log Out"
|
|
destructive
|
|
onPress={async () => {
|
|
await logout();
|
|
nav.go("login");
|
|
}}
|
|
right={null}
|
|
isLast
|
|
/>
|
|
</View>
|
|
</ScrollView>
|
|
|
|
<ThemeModal
|
|
visible={themeSheetVisible}
|
|
current={currentTheme}
|
|
onSelect={(theme) => handleThemeSelect(theme)}
|
|
onClose={() => setThemeSheetVisible(false)}
|
|
/>
|
|
|
|
<LanguageModal
|
|
visible={languageSheetVisible}
|
|
current={currentLanguage}
|
|
onSelect={(lang) => setLanguage(lang)}
|
|
onClose={() => setLanguageSheetVisible(false)}
|
|
/>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|