Skip to content

Commit edc9f47

Browse files
authored
Merge pull request #335 from Dev4w4n/fix-profile
Fix-profile
2 parents e1b295e + 599ee33 commit edc9f47

28 files changed

+4780
-1745
lines changed

apps/profile/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
},
3636
"devDependencies": {
3737
"@masjid-suite/eslint-config": "workspace:*",
38+
"@testing-library/jest-dom": "^6.8.0",
39+
"@testing-library/react": "^14.1.0",
3840
"@types/react": "^18.2.0",
3941
"@types/react-dom": "^18.2.0",
4042
"@typescript-eslint/eslint-plugin": "^6.21.0",

apps/profile/src/App.tsx

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,74 @@ import AdminApplications from "./pages/admin/AdminApplications";
1313
import AdminDashboard from "./pages/admin/AdminDashboard";
1414
import Home from "./pages/Home";
1515

16+
/**
17+
* Auth Route wrapper component - redirects authenticated users away from auth pages
18+
*/
19+
interface AuthRouteProps {
20+
children: React.ReactNode;
21+
}
22+
23+
function AuthRoute({ children }: AuthRouteProps) {
24+
const { user, loading } = useAuth();
25+
26+
if (loading) {
27+
return (
28+
<Box
29+
display="flex"
30+
justifyContent="center"
31+
alignItems="center"
32+
minHeight="100vh"
33+
>
34+
<CircularProgress />
35+
</Box>
36+
);
37+
}
38+
39+
// If user is already authenticated, redirect to home
40+
if (user) {
41+
return <Navigate to="/" replace />;
42+
}
43+
44+
return <>{children}</>;
45+
}
46+
47+
/**
48+
* Protected Route wrapper component
49+
*/
50+
interface ProtectedRouteProps {
51+
children: React.ReactNode;
52+
}
53+
54+
function ProtectedRoute({ children }: ProtectedRouteProps) {
55+
const { user, loading } = useAuth();
56+
57+
if (loading) {
58+
return (
59+
<Box
60+
display="flex"
61+
justifyContent="center"
62+
alignItems="center"
63+
minHeight="100vh"
64+
>
65+
<CircularProgress />
66+
</Box>
67+
);
68+
}
69+
70+
if (!user) {
71+
return <Navigate to="/auth/signin" replace />;
72+
}
73+
74+
return <>{children}</>;
75+
}
76+
1677
/**
1778
* Main App component with authentication and routing
1879
*/
1980
function App() {
20-
const { user, loading } = useAuth();
81+
const { loading } = useAuth();
2182

83+
// Show loading spinner while checking authentication state
2284
if (loading) {
2385
return (
2486
<Box
@@ -31,13 +93,26 @@ function App() {
3193
</Box>
3294
);
3395
}
34-
console.log("App is rendering routes");
3596

3697
return (
3798
<Routes>
3899
{/* Authentication routes (no layout) */}
39-
<Route path="/auth/signin" element={<SignIn />} />
40-
<Route path="/auth/signup" element={<SignUp />} />
100+
<Route
101+
path="/auth/signin"
102+
element={
103+
<AuthRoute>
104+
<SignIn />
105+
</AuthRoute>
106+
}
107+
/>
108+
<Route
109+
path="/auth/signup"
110+
element={
111+
<AuthRoute>
112+
<SignUp />
113+
</AuthRoute>
114+
}
115+
/>
41116

42117
{/* Main application routes (with layout) */}
43118
<Route path="/" element={<Layout />}>
@@ -49,42 +124,52 @@ function App() {
49124
{/* Protected routes */}
50125
<Route
51126
path="profile"
52-
element={user ? <Profile /> : <Navigate to="/auth/signin" replace />}
127+
element={
128+
<ProtectedRoute>
129+
<Profile />
130+
</ProtectedRoute>
131+
}
53132
/>
54133
<Route
55134
path="profile/view"
56135
element={
57-
user ? <ProfileView /> : <Navigate to="/auth/signin" replace />
136+
<ProtectedRoute>
137+
<ProfileView />
138+
</ProtectedRoute>
58139
}
59140
/>
60141
<Route
61142
path="masjids/new"
62143
element={
63-
user ? <MasjidForm /> : <Navigate to="/auth/signin" replace />
144+
<ProtectedRoute>
145+
<MasjidForm />
146+
</ProtectedRoute>
64147
}
65148
/>
66149
<Route
67150
path="masjids/:id/edit"
68151
element={
69-
user ? <MasjidForm /> : <Navigate to="/auth/signin" replace />
152+
<ProtectedRoute>
153+
<MasjidForm />
154+
</ProtectedRoute>
70155
}
71156
/>
72157

73158
{/* Admin routes */}
74159
<Route
75160
path="admin"
76161
element={
77-
user ? <AdminDashboard /> : <Navigate to="/auth/signin" replace />
162+
<ProtectedRoute>
163+
<AdminDashboard />
164+
</ProtectedRoute>
78165
}
79166
/>
80167
<Route
81168
path="admin/applications"
82169
element={
83-
user ? (
170+
<ProtectedRoute>
84171
<AdminApplications />
85-
) : (
86-
<Navigate to="/auth/signin" replace />
87-
)
172+
</ProtectedRoute>
88173
}
89174
/>
90175

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { useState, useEffect } from "react";
2+
import { profileService } from "@masjid-suite/supabase-client";
3+
import { useProfile } from "@masjid-suite/auth";
4+
import type { Database } from "@masjid-suite/shared-types";
5+
6+
type ProfileAddress = Database["public"]["Tables"]["profile_addresses"]["Row"];
7+
type ProfileAddressInsert =
8+
Database["public"]["Tables"]["profile_addresses"]["Insert"];
9+
type ProfileAddressUpdate =
10+
Database["public"]["Tables"]["profile_addresses"]["Update"];
11+
12+
/**
13+
* Hook for managing user addresses
14+
*/
15+
export function useAddresses() {
16+
const { profile } = useProfile();
17+
const [addresses, setAddresses] = useState<ProfileAddress[]>([]);
18+
const [loading, setLoading] = useState(false);
19+
const [error, setError] = useState<string | null>(null);
20+
21+
// Load addresses when profile is available
22+
useEffect(() => {
23+
if (profile?.id) {
24+
loadAddresses();
25+
}
26+
}, [profile?.id]);
27+
28+
const loadAddresses = async () => {
29+
if (!profile?.id) return;
30+
31+
try {
32+
setLoading(true);
33+
setError(null);
34+
const data = await profileService.getProfileAddresses(profile.id);
35+
setAddresses(data);
36+
} catch (err) {
37+
console.error("Failed to load addresses:", err);
38+
setError(err instanceof Error ? err.message : "Failed to load addresses");
39+
} finally {
40+
setLoading(false);
41+
}
42+
};
43+
44+
const addAddress = async (
45+
addressData: Omit<ProfileAddressInsert, "profile_id">
46+
) => {
47+
if (!profile?.id) {
48+
throw new Error("No profile available");
49+
}
50+
51+
try {
52+
setLoading(true);
53+
setError(null);
54+
55+
const newAddress = await profileService.addProfileAddress({
56+
...addressData,
57+
profile_id: profile.id,
58+
});
59+
60+
setAddresses((prev) => [...prev, newAddress]);
61+
return newAddress;
62+
} catch (err) {
63+
console.error("Failed to add address:", err);
64+
const errorMessage =
65+
err instanceof Error ? err.message : "Failed to add address";
66+
setError(errorMessage);
67+
throw new Error(errorMessage);
68+
} finally {
69+
setLoading(false);
70+
}
71+
};
72+
73+
const updateAddress = async (
74+
addressId: string,
75+
updates: ProfileAddressUpdate
76+
) => {
77+
try {
78+
setLoading(true);
79+
setError(null);
80+
81+
const updatedAddress = await profileService.updateProfileAddress(
82+
addressId,
83+
updates
84+
);
85+
86+
setAddresses((prev) =>
87+
prev.map((addr) => (addr.id === addressId ? updatedAddress : addr))
88+
);
89+
90+
return updatedAddress;
91+
} catch (err) {
92+
console.error("Failed to update address:", err);
93+
const errorMessage =
94+
err instanceof Error ? err.message : "Failed to update address";
95+
setError(errorMessage);
96+
throw new Error(errorMessage);
97+
} finally {
98+
setLoading(false);
99+
}
100+
};
101+
102+
const deleteAddress = async (addressId: string) => {
103+
try {
104+
setLoading(true);
105+
setError(null);
106+
107+
await profileService.deleteProfileAddress(addressId);
108+
109+
setAddresses((prev) => prev.filter((addr) => addr.id !== addressId));
110+
} catch (err) {
111+
console.error("Failed to delete address:", err);
112+
const errorMessage =
113+
err instanceof Error ? err.message : "Failed to delete address";
114+
setError(errorMessage);
115+
throw new Error(errorMessage);
116+
} finally {
117+
setLoading(false);
118+
}
119+
};
120+
121+
const setPrimaryAddress = async (addressId: string) => {
122+
if (!profile?.id) {
123+
throw new Error("No profile available");
124+
}
125+
126+
try {
127+
setLoading(true);
128+
setError(null);
129+
130+
await profileService.setPrimaryAddress(profile.id, addressId);
131+
132+
// Update local state to reflect the change
133+
setAddresses((prev) =>
134+
prev.map((addr) => ({
135+
...addr,
136+
is_primary: addr.id === addressId,
137+
}))
138+
);
139+
} catch (err) {
140+
console.error("Failed to set primary address:", err);
141+
const errorMessage =
142+
err instanceof Error ? err.message : "Failed to set primary address";
143+
setError(errorMessage);
144+
throw new Error(errorMessage);
145+
} finally {
146+
setLoading(false);
147+
}
148+
};
149+
150+
const primaryAddress = addresses.find((addr) => addr.is_primary);
151+
const nonPrimaryAddresses = addresses.filter((addr) => !addr.is_primary);
152+
153+
return {
154+
addresses,
155+
primaryAddress,
156+
nonPrimaryAddresses,
157+
loading,
158+
error,
159+
addAddress,
160+
updateAddress,
161+
deleteAddress,
162+
setPrimaryAddress,
163+
refreshAddresses: loadAddresses,
164+
};
165+
}

0 commit comments

Comments
 (0)