project setup

This commit is contained in:
Sone
2025-11-02 10:08:56 +01:00
commit 568838003e
174 changed files with 27100 additions and 0 deletions
+307
View File
@@ -0,0 +1,307 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { usePathname } from "expo-router";
import { useEffect, useRef, useState } from "react";
import { Animated, Platform, View } from "react-native";
import { useAppSelector, useAppDispatch } from "@/store/hooks";
import {
selectIsMobile,
setSidebarCollapsed,
} from "@/store/screen/screenSizeSlice";
import { SignOutButton } from "@/components/auth/SignOutButton";
import { useAuthContext } from "@/auth/AuthContext";
import { SidebarToggle } from "@/components/features/navigation/SidebarToggle";
import { SidebarItem } from "@/components/features/navigation/SidebarItem";
const SIDEBAR_STATE_KEY = "sidebarCollapsedState";
const saveCollapsedState = async (isCollapsed: boolean) => {
try {
if (Platform.OS === "web") {
localStorage.setItem(SIDEBAR_STATE_KEY, JSON.stringify(isCollapsed));
} else {
await AsyncStorage.setItem(
SIDEBAR_STATE_KEY,
JSON.stringify(isCollapsed)
);
}
} catch (error) {
console.error("Error saving sidebar state:", error);
}
};
const loadCollapsedState = async (): Promise<boolean | null> => {
try {
let value;
if (Platform.OS === "web") {
value = localStorage.getItem(SIDEBAR_STATE_KEY);
} else {
value = await AsyncStorage.getItem(SIDEBAR_STATE_KEY);
}
return value ? JSON.parse(value) : null;
} catch (error) {
console.error("Error loading sidebar state:", error);
return null;
}
};
export const Sidebar = ({
mobileHidden = false,
onRequestMobileToggle,
}: {
mobileHidden?: boolean;
onRequestMobileToggle?: () => void;
}) => {
const { isAuthenticated } = useAuthContext();
const pathname = usePathname();
const [collapsed, setCollapsed] = useState(false);
const [labelsVisible, setLabelsVisible] = useState(true);
const animatedWidth = useRef(new Animated.Value(200)).current;
const textOpacity = useRef(new Animated.Value(1)).current;
const iconTranslateX = useRef(new Animated.Value(0)).current;
const paddingLeft = useRef(new Animated.Value(16)).current; // 0.5rem = 8px
const isMobile = useAppSelector(selectIsMobile);
const dispatch = useAppDispatch();
useEffect(() => {
const loadSavedState = async () => {
if (isMobile) {
setCollapsed(true);
setLabelsVisible(false);
textOpacity.setValue(0);
iconTranslateX.setValue(12);
paddingLeft.setValue(8);
Animated.timing(animatedWidth, {
toValue: mobileHidden ? 0 : 80,
duration: 250,
useNativeDriver: false,
}).start();
return;
}
const savedState = await loadCollapsedState();
if (savedState !== null) {
setCollapsed(savedState);
dispatch(setSidebarCollapsed(savedState));
setLabelsVisible(!savedState);
textOpacity.setValue(savedState ? 0 : 1);
iconTranslateX.setValue(savedState ? 12 : 0);
paddingLeft.setValue(8);
Animated.timing(animatedWidth, {
toValue: savedState ? 80 : 200,
duration: 300,
useNativeDriver: false,
}).start();
}
};
loadSavedState();
}, [
animatedWidth,
textOpacity,
iconTranslateX,
paddingLeft,
isMobile,
mobileHidden,
dispatch,
]);
const isActive = (path: string) => {
if (path === "/user-dashboard" || path === "/dashboard") {
return (
pathname.includes("/user-dashboard") ||
pathname === "/(tabs)" ||
pathname === "/"
);
}
if (path === "/accounts") {
return pathname.includes("/accounts");
}
if (path === "/settings") {
return (
pathname.startsWith("/settings") ||
pathname.startsWith("/(tabs)/settings")
);
}
if (path === "/data-protection") {
return (
pathname.startsWith("/data-protection") ||
pathname.startsWith("/(tabs)/data-protection")
);
}
return pathname.includes(path);
};
const toggleSidebar = () => {
if (isMobile) {
onRequestMobileToggle?.();
return;
}
const newCollapsedState = !collapsed;
if (!newCollapsedState) {
setLabelsVisible(true);
}
Animated.parallel([
Animated.timing(animatedWidth, {
toValue: newCollapsedState ? 80 : 200,
duration: 300,
useNativeDriver: false,
}),
Animated.timing(textOpacity, {
toValue: newCollapsedState ? 0 : 1,
duration: 250,
useNativeDriver: true,
}),
Animated.timing(iconTranslateX, {
toValue: newCollapsedState ? 12 : 0,
duration: 300,
useNativeDriver: true,
}),
Animated.timing(paddingLeft, {
toValue: 8,
duration: 300,
useNativeDriver: false,
}),
]).start(() => {
setLabelsVisible(!newCollapsedState);
});
if (newCollapsedState) {
setLabelsVisible(true);
}
setCollapsed(newCollapsedState);
dispatch(setSidebarCollapsed(newCollapsedState));
saveCollapsedState(newCollapsedState);
};
return (
<Animated.View
className="border-r px-2"
style={{
width: animatedWidth,
backgroundColor: "#49385D",
borderRightColor: "#49385D",
borderRightWidth: undefined,
paddingHorizontal: undefined,
flex: isMobile ? undefined : 1,
height: "100%",
overflow: "hidden",
}}
>
<View className="flex-1 flex-col">
{!(isMobile && mobileHidden) && (
<View
className="border-b-2 border-white"
style={{
paddingBottom: isMobile ? 6 : 0,
paddingTop: isMobile ? 6 : 0,
}}
>
<SidebarToggle
onPress={toggleSidebar}
textOpacity={textOpacity}
iconTranslateX={iconTranslateX}
showLabel={labelsVisible}
className="h-[40px] w-full justify-start"
/>
</View>
)}
{!(isMobile && mobileHidden) && (
<View>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/dashboard/user-dashboard"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/dashboard/user-dashboard"
icon="home-outline"
label="Home"
isFirst
/>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/(tabs)/accounts/users"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/accounts"
icon="person-outline"
label="Accounts"
/>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/(tabs)/logs/system"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/logs"
icon="checkbox-outline"
label="Logs"
/>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/(tabs)/reports/inbox"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/reports"
icon="copy-outline"
label="Reports"
/>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/(tabs)/data-protection/general"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/data-protection"
icon="lock-closed-outline"
label="Data Protection"
/>
<SidebarItem
paddingLeft={paddingLeft}
routerPushValue={"/(tabs)/settings/general"}
isMobile={isMobile}
isActive={isActive}
iconTranslateX={iconTranslateX}
textOpacity={textOpacity}
showLabel={labelsVisible}
path="/settings"
icon="settings-outline"
label="Settings"
/>
</View>
)}
{!(isMobile && mobileHidden) && (
<View className="mb-4 mt-auto px-2">
{isAuthenticated && (
<SignOutButton
textOpacity={textOpacity}
iconTranslateX={iconTranslateX}
paddingLeft={paddingLeft}
/>
)}
</View>
)}
</View>
</Animated.View>
);
};