diff --git a/app/(auth)/index.tsx b/app/(auth)/index.tsx
index 1631ef2..19a94e6 100644
--- a/app/(auth)/index.tsx
+++ b/app/(auth)/index.tsx
@@ -6,11 +6,10 @@ import {
TouchableOpacity,
StyleSheet,
Dimensions,
- Alert,
+ Alert, Platform,
} from 'react-native';
import {useRouter} from "expo-router";
import {supabase} from "@/lib/supabaseClient";
-import * as SecureStore from 'expo-secure-store';
const { width } = Dimensions.get('window'); // Get the current screen width
@@ -44,31 +43,13 @@ export default function LoginPage() {
if (data?.user) {
console.log("Signed in user:", data.user);
+ console.log("Session data:", data.session);
- // Fetch role using the user ID directly
- if (!supabase) {
- Alert.alert("Error", "Supabase client is not initialized.");
- return;
- }
- const { data: roleData, error: roleError } = await supabase
- .from('profiles')
- .select('role')
- .eq('profile_id', data.user.id)
- .single();
-
- if (roleError) {
- console.error('Error fetching role:', roleError);
- } else {
- console.log('User role:', roleData);
- await SecureStore.setItemAsync('role', roleData.role?.toLowerCase());
- }
-
- // Navigate after everything is ready
+ // Navigate after successful login
router.replace('/(tabs)');
}
}
-
await signInWithEmail()
}
diff --git a/app/(auth)/signuppage.tsx b/app/(auth)/signuppage.tsx
index 6427821..b290842 100644
--- a/app/(auth)/signuppage.tsx
+++ b/app/(auth)/signuppage.tsx
@@ -97,7 +97,6 @@ export default function SignUpPage() {
}
try {
- // Sign up with Supabase
const { data: authData, error: authError } = await supabase.auth.signUp({
email: formData.email,
password: formData.password,
@@ -116,16 +115,21 @@ export default function SignUpPage() {
}
if (authData.user) {
- Alert.alert(
- 'Success',
- 'Account created successfully! Please check your email for verification.',
- [
- {
- text: 'OK',
- onPress: () => router.replace('/(auth)'),
- },
- ]
- );
+ if (Platform.OS === 'web') {
+ window.alert('Account created successfully! Please check your email for verification.');
+ router.replace('/(auth)');
+ } else {
+ Alert.alert(
+ 'Success',
+ 'Account created successfully! Please check your email for verification.',
+ [
+ {
+ text: 'OK',
+ onPress: () => router.replace('/(auth)'),
+ },
+ ]
+ );
+ }
}
} catch (error) {
console.error('Sign up error:', error);
@@ -218,7 +222,6 @@ export default function SignUpPage() {
Sign Up
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index ca5d88a..10b8ddd 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -1,86 +1,117 @@
+import { Stack } from 'expo-router';
+import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
+import { useRouter, usePathname } from 'expo-router';
import FontAwesome from '@expo/vector-icons/FontAwesome';
-import { Tabs } from 'expo-router';
-import {useEffect, useState} from "react";
-import * as SecureStore from "expo-secure-store";
+import { useEffect, useState } from 'react';
+import { supabase } from '@/lib/supabaseClient';
export default function TabLayout() {
+ const router = useRouter();
+ const pathname = usePathname();
const [role, setRole] = useState(null);
useEffect(() => {
const fetchRole = async () => {
- const storedRole = await SecureStore.getItemAsync('role');
- setRole(storedRole);
+ if (supabase) {
+ const { data: { session } } = await supabase.auth.getSession();
+ if (session?.user) {
+ const { data: roleData } = await supabase
+ .from('profiles')
+ .select('role')
+ .eq('profile_id', session.user.id)
+ .single();
+
+ if (roleData?.role) {
+ setRole(roleData.role.toLowerCase());
+ }
+ }
+ }
};
fetchRole();
}, []);
- const isAdmin = role === 'administrator' || role === 'supervisor';
+
+ const isAdmin = role === 'admin';
+
+ const tabs = [
+ { name: 'index', title: 'Schedule', icon: 'calendar-check-o' },
+ ...(isAdmin ? [{ name: 'add-shift', title: 'Add Shift', icon: 'calendar-plus-o' }] : []),
+ { name: 'coworkers', title: 'Coworkers', icon: 'users' },
+ { name: 'notifications', title: 'Notifications', icon: 'bell' },
+ { name: 'profileView', title: 'Profile', icon: 'user' },
+ ];
+
+ const handleTabPress = (tabName: string) => {
+ if (tabName === 'index') {
+ router.push('/(tabs)' as any);
+ } else {
+ router.push(`/(tabs)/${tabName}` as any);
+ }
+ };
+
+ const isTabActive = (tabName: string) => {
+ if (tabName === 'index') {
+ return pathname === '/(tabs)/' || pathname === '/(tabs)/index';
+ }
+ return pathname === `/(tabs)/${tabName}`;
+ };
+
return (
-
- ,
- }}
- />
- ,
- href: isAdmin ? null : '/(tabs)/add-shift'
- }}
- />
- ,
- }}
- />
- ,
- }}
- />
- ,
- }}
- />
- {/* These screens are not direct tabs and are handled by file-system routing or other navigation methods */}
- {/* Remove the explicit definition for shift-details-page/[id] */}
- {/*
-
-
-
-
- */}
-
+
+
+
+
+
+
+
+
+
+ {/* Custom Bottom Tab Bar */}
+
+ {tabs.map((tab) => {
+ const isActive = isTabActive(tab.name);
+ return (
+ handleTabPress(tab.name)}
+ >
+
+
+ {tab.title}
+
+
+ );
+ })}
+
+
);
-}
\ No newline at end of file
+}
+
+const styles = StyleSheet.create({
+ tabBar: {
+ flexDirection: 'row',
+ backgroundColor: '#fff',
+ borderTopWidth: 1,
+ borderTopColor: '#E5E5EA',
+ paddingBottom: 20,
+ paddingTop: 10,
+ },
+ tab: {
+ flex: 1,
+ alignItems: 'center',
+ paddingVertical: 8,
+ },
+ tabText: {
+ fontSize: 12,
+ marginTop: 4,
+ color: '#8E8E93',
+ },
+ activeTabText: {
+ color: '#007AFF',
+ },
+});
\ No newline at end of file
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index e4b471d..d6e40b7 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -101,9 +101,15 @@ export default function UserDashboard() {
useEffect(() => {
const fetchShiftData = async () => {
if (supabase != null) {
- const shifts = await getAll(supabase, 'shifts');
- if (shifts)
- setShiftData(shifts as Shift[]);
+ // Check if user is authenticated
+ const { data: { session } } = await supabase.auth.getSession();
+ if (session && session.user) {
+ const shifts = await getAll(supabase, 'shifts');
+ if (shifts)
+ setShiftData(shifts as Shift[]);
+ } else {
+ console.log('No authenticated session found');
+ }
}
}
fetchShiftData();
diff --git a/app/(tabs)/profileView.tsx b/app/(tabs)/profileView.tsx
index 0a6014f..3be75bb 100644
--- a/app/(tabs)/profileView.tsx
+++ b/app/(tabs)/profileView.tsx
@@ -23,19 +23,31 @@ const ProfileView = () => {
const [error, setError] = useState(null);
const router = useRouter();
- // Using the profile_int_id provided
- const profileIntId = 5;
-
const fetchProfileById = useCallback(async () => {
setLoading(true);
setError(null);
try {
if (supabase) {
+ // Get the current session
+ const { data: { session }, error: sessionError } = await supabase.auth.getSession();
+
+ if (sessionError) {
+ console.error('Error getting session:', sessionError);
+ setError(sessionError.message);
+ return;
+ }
+
+ if (!session || !session.user) {
+ setError('No active session found. Please log in again.');
+ return;
+ }
+
+ // Fetch profile using the user's ID from the session
const { data, error } = await supabase
.from('profiles')
.select('*')
- .eq('profile_int_id', profileIntId)
+ .eq('profile_id', session.user.id)
.single();
if (error) {
@@ -59,9 +71,23 @@ const ProfileView = () => {
fetchProfileById();
}, [fetchProfileById]);
- const handleLogout = () => {
- router.replace('/(auth)'); // Navigate to the login page
- console.log('Logout pressed');
+ const handleLogout = async () => {
+ try {
+ if (supabase) {
+ const { error } = await supabase.auth.signOut();
+ if (error) {
+ console.error('Error signing out:', error);
+ } else {
+ console.log('Successfully signed out');
+ router.replace('/(auth)');
+ }
+ } else {
+ router.replace('/(auth)');
+ }
+ } catch (error) {
+ console.error('Error during logout:', error);
+ router.replace('/(auth)');
+ }
};
const handleEditProfile = () => {
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 2d1da3c..b84d344 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -1,5 +1,28 @@
import { Stack } from "expo-router";
-import { Slot } from "expo-router";
+import { useEffect } from "react";
+import { useColorScheme } from "react-native";
+import * as SplashScreen from "expo-splash-screen";
+
+// Keep the splash screen visible while we fetch resources
+SplashScreen.preventAutoHideAsync();
+
export default function RootLayout() {
- return ();
+ const colorScheme = useColorScheme();
+
+ useEffect(() => {
+ SplashScreen.hideAsync();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
}
diff --git a/app/index.tsx b/app/index.tsx
index ff2954c..31c9a71 100644
--- a/app/index.tsx
+++ b/app/index.tsx
@@ -94,4 +94,4 @@ const styles = StyleSheet.create({
textAlign: "left",
marginTop: 20,
},
-});
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/hooks/shift-navigation.tsx b/hooks/shift-navigation.tsx
index 2192153..8455397 100644
--- a/hooks/shift-navigation.tsx
+++ b/hooks/shift-navigation.tsx
@@ -16,11 +16,7 @@ export const useShiftNavigation = (currentShiftId: number) => {
};
const showAlert = (title: string, message: string): void => {
- if (Platform.OS === 'web') {
- window.alert(`${title}\n${message}`);
- } else {
- Alert.alert(title, message);
- }
+ Alert.alert(title, message);
};
const goToPreviousShift = () => {
diff --git a/lib/expoSecureStoreAdapter.ts b/lib/expoSecureStoreAdapter.ts
index 887a100..43e39a3 100644
--- a/lib/expoSecureStoreAdapter.ts
+++ b/lib/expoSecureStoreAdapter.ts
@@ -1,34 +1,132 @@
import * as SecureStore from "expo-secure-store";
+import { Platform } from 'react-native';
+
+const isWeb = Platform.OS === 'web';
+const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
+
+// Cookie storage for web platform
+const getCookie = (name: string): string | null => {
+ if (!isWeb || !isBrowser) return null;
+ const value = `; ${document.cookie}`;
+ const parts = value.split(`; ${name}=`);
+ if (parts.length === 2) {
+ const cookieValue = parts.pop()?.split(';').shift();
+ return cookieValue ? decodeURIComponent(cookieValue) : null;
+ }
+ return null;
+};
+
+const setCookie = (name: string, value: string, days = 7): void => {
+ if (!isWeb || !isBrowser) return;
+ const expires = new Date();
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
+ document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/`;
+};
+
+const deleteCookie = (name: string): void => {
+ if (!isWeb || !isBrowser) return;
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
+};
+
+// Memory storage as fallback for non-browser environments
+const memoryStorage = new Map();
export const ExpoSecureStoreAdapter = {
getItem: async (key: string) => {
try {
- const value = await SecureStore.getItemAsync(key);
+ let value;
+ if (isWeb) {
+ if (isBrowser) {
+ value = getCookie(key);
+ } else {
+ value = memoryStorage.get(key);
+ }
+ } else {
+ value = await SecureStore.getItemAsync(key);
+ }
+
if (value) {
- const { access_token, refresh_token } = JSON.parse(value);
- return JSON.stringify({access_token, refresh_token, token_type: 'bearer', user: null})
+ // Try to parse as JSON first (for session data)
+ try {
+ const parsed = JSON.parse(value);
+ if (parsed.access_token && parsed.refresh_token) {
+ // This is session data
+ const { access_token, refresh_token } = parsed;
+ return JSON.stringify({access_token, refresh_token, token_type: 'bearer', user: null})
+ } else {
+ // This is not session data, return as simple string
+ return value;
+ }
+ } catch {
+ // Not valid JSON, return as simple string
+ return value;
+ }
}
return null;
} catch (error) {
- console.error('Error getting item from Securestore:', error);
+ console.error('Error getting item from storage:', error);
return null;
}
},
setItem: async (key: string, value: string): Promise => {
try {
- const session = JSON.parse(value);
- const { access_token, refresh_token } = session;
- // Store only the tokens in SecureStore
- await SecureStore.setItemAsync(key, JSON.stringify({ access_token, refresh_token }));
+ let storageValue;
+
+ // Try to parse as JSON first (for session data)
+ try {
+ const session = JSON.parse(value);
+ if (session.access_token && session.refresh_token) {
+ // This is session data
+ const { access_token, refresh_token } = session;
+ storageValue = JSON.stringify({ access_token, refresh_token });
+ } else {
+ // This is not session data, store as simple string
+ storageValue = value;
+ }
+ } catch {
+ // Not valid JSON, store as simple string
+ storageValue = value;
+ }
+
+ if (isWeb) {
+ if (isBrowser) {
+ setCookie(key, storageValue);
+ } else {
+ memoryStorage.set(key, storageValue);
+ }
+ } else {
+ await SecureStore.setItemAsync(key, storageValue);
+ }
} catch (error) {
- console.error('Error setting item to Securestore:', error);
+ console.error('Error setting item to storage:', error);
}
},
removeItem: async (key: string): Promise => {
try {
- await SecureStore.deleteItemAsync(key);
+ if (isWeb) {
+ if (isBrowser) {
+ deleteCookie(key);
+ } else {
+ memoryStorage.delete(key);
+ }
+ } else {
+ await SecureStore.deleteItemAsync(key);
+ }
} catch (error) {
- console.error('Error deleting item from Securestore:', error);
+ console.error('Error deleting item from storage:', error);
}
},
-};
\ No newline at end of file
+};
+
+if (isWeb) {
+ // Polyfill for Supabase Auth JS expecting these methods
+ (ExpoSecureStoreAdapter as any).setValueWithKeyAsync = async (key: string, value: string) => {
+ return ExpoSecureStoreAdapter.setItem(key, value);
+ };
+ (ExpoSecureStoreAdapter as any).getValueWithKeyAsync = async (key: string) => {
+ return ExpoSecureStoreAdapter.getItem(key);
+ };
+ (ExpoSecureStoreAdapter as any).deleteValueWithKeyAsync = async (key: string) => {
+ return ExpoSecureStoreAdapter.removeItem(key);
+ };
+}
\ No newline at end of file
diff --git a/lib/supabaseClient.ts b/lib/supabaseClient.ts
index 65460f0..00263c9 100644
--- a/lib/supabaseClient.ts
+++ b/lib/supabaseClient.ts
@@ -1,7 +1,4 @@
-// @ts-ignore
-import * as SecureStore from 'expo-secure-store';
import { createClient } from '@supabase/supabase-js';
-import {ExpoSecureStoreAdapter} from "@/lib/expoSecureStoreAdapter";
const SUPABASE_URL = process.env.EXPO_PUBLIC_SUPABASE_URL;
const SUPABASE_ANON_KEY = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY;
@@ -13,7 +10,6 @@ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
export const supabase = SUPABASE_URL && SUPABASE_ANON_KEY
? createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: {
- storage: ExpoSecureStoreAdapter,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,