Skip to content

Commit c71f111

Browse files
implement admin features
1 parent 795a897 commit c71f111

File tree

15 files changed

+242
-170
lines changed

15 files changed

+242
-170
lines changed

frontend/src/api/queries/Admin.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import type {
88
ContributionScore,
99
ContributionScoreUpdate
1010
} from "@/shared/types/types";
11+
import {
12+
CONTRIBUTION_TYPES_QUERY_KEY,
13+
GET_ALL_USERS_QUERY_KEY
14+
} from "@/shared/constants/query-keys";
1115

1216
const LogInAdmin = async (
1317
adminCredentials: AdminCredentials
@@ -41,7 +45,7 @@ const getAllUsers = async (): Promise<ApiResponse<Admin[]>> => {
4145

4246
export const useGetAllUsers = () => {
4347
return useQuery({
44-
queryKey: ["getAllUsers"],
48+
queryKey: [GET_ALL_USERS_QUERY_KEY],
4549
queryFn: getAllUsers
4650
});
4751
};
@@ -80,7 +84,7 @@ const fetchContributionTypes = async (): Promise<
8084

8185
export const useFetchContributionTypes = () => {
8286
return useQuery({
83-
queryKey: ["fetch-contribution-types"],
87+
queryKey: [CONTRIBUTION_TYPES_QUERY_KEY],
8488
queryFn: fetchContributionTypes
8589
});
8690
};

frontend/src/api/queries/UserProfileDetails.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ const fetchLoggedInUser = async (): Promise<ApiResponse<User>> => {
1414
return response.data;
1515
};
1616

17-
export const useLoggedInUser = () => {
17+
export const useLoggedInUser = (enabled: boolean = true) => {
1818
return useQuery({
1919
queryKey: [LOGGED_IN_USER_QUERY_KEY],
20-
queryFn: fetchLoggedInUser
20+
queryFn: fetchLoggedInUser,
21+
enabled,
22+
retry: false
2123
});
2224
};
2325

Lines changed: 1 addition & 0 deletions
Loading

frontend/src/features/Admin/AdminLogin.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,24 @@ import { useLogInAdmin } from "@/api/queries/Admin";
44
import { useForm } from "react-hook-form";
55
import { useNavigate } from "react-router-dom";
66
import { ACCESS_TOKEN_KEY } from "@/shared/constants/local-storage";
7+
import { Button } from "@/shared/components/ui/button";
78

89
const AdminLogin: FC = () => {
910
const {
1011
register,
1112
handleSubmit,
1213
formState: { errors }
1314
} = useForm<AdminCredentials>();
14-
1515
const navigate = useNavigate();
16-
1716
const { mutate, isPending, isError, error } = useLogInAdmin();
17+
1818
const onSubmit = async (
1919
data: AdminCredentials,
2020
event?: React.BaseSyntheticEvent
2121
) => {
2222
try {
2323
event?.preventDefault();
2424
console.log(" Submitting admin login", data);
25-
2625
mutate(data, {
2726
onSuccess: res => {
2827
console.log(" Admin login success", res);
@@ -53,7 +52,7 @@ const AdminLogin: FC = () => {
5352
<input
5453
id="email"
5554
type="email"
56-
className="w-full rounded-md border border-gray-300 p-2 text-sm shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
55+
className="w-full rounded-md border border-gray-300 bg-white p-2 text-sm shadow-sm focus:border-blue-500 focus:bg-white focus:ring-0 focus:outline-none"
5756
{...register("email", { required: "Email is required" })}
5857
/>
5958
{errors.email && (
@@ -71,7 +70,7 @@ const AdminLogin: FC = () => {
7170
<input
7271
id="password"
7372
type="password"
74-
className="w-full rounded-md border border-gray-300 p-2 text-sm shadow-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
73+
className="w-full rounded-md border border-gray-300 bg-white p-2 text-sm shadow-sm focus:border-blue-500 focus:bg-white focus:ring-0 focus:outline-none"
7574
{...register("password", { required: "Password is required" })}
7675
/>
7776
{errors.password && (
@@ -81,19 +80,19 @@ const AdminLogin: FC = () => {
8180

8281
{isError && (
8382
<p className="text-sm text-red-600">
84-
{(error as Error)?.message || "Failed to log in"}
83+
{error?.message || "Failed to log in"}
8584
</p>
8685
)}
8786

88-
<button
87+
<Button
8988
type="submit"
9089
disabled={isPending}
91-
className="w-full rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"
90+
className="bg-cc-app-orange w-full rounded-md px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"
9291
>
9392
{isPending ? "Logging in..." : "Log in as Admin"}
94-
</button>
93+
</Button>
9594
</form>
9695
);
9796
};
9897

99-
export default AdminLogin;
98+
export default AdminLogin;

frontend/src/features/Admin/ScoreConfigure.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useState } from "react";
22

33
import { Button } from "@/shared/components/ui/button";
44
import { Input } from "@/shared/components/ui/input";
5-
import { Card } from "@/shared/components/ui/card";
65
import {
76
useFetchContributionTypes,
87
useConfigureContributionScore
@@ -53,7 +52,7 @@ const ScoreConfigure = () => {
5352
};
5453

5554
return (
56-
<Card className="mx-auto max-w-4xl p-6">
55+
<div className="mx-auto max-w-4xl p-6">
5756
<div className="mb-6">
5857
<h1 className="mb-2 text-2xl font-bold text-gray-900">
5958
Configure Contribution Scores
@@ -86,7 +85,7 @@ const ScoreConfigure = () => {
8685
{isPending ? "Saving..." : "Save All"}
8786
</Button>
8887
</div>
89-
</Card>
88+
</div>
9089
);
9190
};
9291

frontend/src/features/Login/components/LoginComponent.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,31 @@ import { useEffect } from "react";
1212
import { setAccessToken } from "@/shared/utils/local-storage";
1313
import { useGithubOauthLogin } from "@/api/queries/Auth";
1414
import { toast } from "sonner";
15-
15+
import { useLoggedInUser } from "@/api/queries/UserProfileDetails";
16+
import { ACCOUNT_INFO_PATH } from "@/shared/constants/routes";
17+
import githubIcon from "@/assets/github-white-icon.svg";
1618
const LoginComponent = () => {
1719
const [searchParams] = useSearchParams();
1820
const navigate = useNavigate();
1921

2022
const code = searchParams.get("code");
2123
const { data, isSuccess, isError } = useGithubOauthLogin(code);
2224

25+
const { refetch } = useLoggedInUser(false);
26+
2327
useEffect(() => {
2428
if (!code) return;
2529

2630
if (isSuccess && data?.data) {
2731
const token = data.data;
2832
setAccessToken(token);
33+
34+
refetch().then(({ data: userData }) => {
35+
if (userData?.data?.isBlocked) {
36+
navigate(ACCOUNT_INFO_PATH);
37+
}
38+
});
39+
2940
navigate("/");
3041
}
3142

@@ -47,9 +58,9 @@ const LoginComponent = () => {
4758
<CardContent className="space-y-6">
4859
<Button
4960
onClick={handleGithubLogin}
50-
className="bg-cc-app-orange h-10 w-3/4 rounded-md font-semibold text-white"
61+
className="bg-cc-app-orange hover:bg-cc-app-blue h-10 w-3/4 rounded-md font-semibold text-white"
5162
>
52-
Login via GitHub
63+
<img src={githubIcon} className="h-4 w-4" /> Sign in with GitHub
5364
</Button>
5465
<hr className="border-gray-200" />
5566
</CardContent>

frontend/src/root/routes-config.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import Login from "@/features/Login";
44
import MyContributions from "@/features/MyContributions";
55
import UserDashboard from "@/features/UserDashboard";
66
import {
7+
ACCOUNT_INFO_PATH,
8+
ADMIN_LOGIN_PATH,
9+
ADMIN_SCORE_CONFIGURE_PATH,
10+
ADMIN_USERS_PATH,
711
LOGIN_PATH,
812
MY_CONTRIBUTIONS_PATH,
913
REPOSITORY_DETAILS_PATH,
@@ -13,6 +17,7 @@ import RepositoryDetails from "@/features/RepositoryDetails.tsx";
1317
import AdminLogin from "@/features/Admin/AdminLogin";
1418
import { AllUsersList } from "@/features/Admin/Users.tsx";
1519
import ScoreConfigure from "@/features/Admin/ScoreConfigure";
20+
import BlockedAccountPage from "@/shared/components/common/BlockedAccountPage";
1621
export interface RoutesType {
1722
path: string;
1823
element: ReactNode;
@@ -46,21 +51,27 @@ export const routesConfig: RoutesType[] = [
4651
layout: Layout.DashboardLayout
4752
},
4853
{
49-
path: "/admin/login",
54+
path: ADMIN_LOGIN_PATH,
5055
element: <AdminLogin />,
5156
isProtected: false,
5257
layout: Layout.AuthLayout
5358
},
5459
{
55-
path: "/admin/users",
60+
path: ADMIN_USERS_PATH,
5661
element: <AllUsersList />,
5762
isProtected: true,
5863
layout: Layout.AdminLayout
5964
},
6065
{
61-
path: "/admin/configure/score",
66+
path: ADMIN_SCORE_CONFIGURE_PATH,
6267
element: <ScoreConfigure />,
6368
isProtected: true,
6469
layout: Layout.AdminLayout
70+
},
71+
{
72+
path: ACCOUNT_INFO_PATH,
73+
element: <BlockedAccountPage />,
74+
isProtected: false,
75+
layout: Layout.None
6576
}
6677
];

frontend/src/shared/HOC/WithAuth.tsx

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,4 @@ const WithAuth: FC<WithAuthProps> = ({ children }) => {
2626
return <>{children}</>;
2727
};
2828

29-
export default WithAuth;
30-
31-
32-
33-
34-
// import { useEffect } from 'react'
35-
// import { useNavigate, useSearchParams } from 'react-router-dom'
36-
37-
// export default function AuthCallback() {
38-
// const [searchParams] = useSearchParams()
39-
// const navigate = useNavigate()
40-
41-
// useEffect(() => {
42-
// const code = searchParams.get('code')
43-
// if (!code) return
44-
45-
// // Call backend to exchange code for token
46-
// fetch(`http://localhost:8080/github/callback?code=${code}`)
47-
// .then(res => res.json())
48-
// .then(data => {
49-
// const token = data.accessToken
50-
// if (token) {
51-
// // Store token in localStorage (or sessionStorage)
52-
// localStorage.setItem('accessToken', token)
53-
54-
// // Navigate to home or dashboard
55-
// navigate('/dashboard')
56-
// } else {
57-
// console.error('No token received')
58-
// }
59-
// })
60-
// .catch(err => {
61-
// console.error('GitHub login failed:', err)
62-
// })
63-
// }, [])
64-
65-
// return <div>Logging in...</div>
66-
// }
29+
export default WithAuth;

frontend/src/shared/components/UserDashboard/DeleteAccount.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ import {
1111
useLoggedInUser,
1212
useSoftDeleteUser
1313
} from "@/api/queries/UserProfileDetails";
14+
import { clearAccessToken } from "@/shared/utils/local-storage";
15+
import { useNavigate } from "react-router-dom";
1416

1517
interface Props {
1618
open: boolean;
1719
onClose: () => void;
1820
}
1921

2022
const DeleteAccount = ({ open, onClose }: Props) => {
23+
const navigate = useNavigate();
2124
const { data } = useLoggedInUser();
2225
const user = data?.data;
2326
const { mutate: softDeleteUser, isPending } = useSoftDeleteUser();
@@ -27,6 +30,8 @@ const DeleteAccount = ({ open, onClose }: Props) => {
2730
softDeleteUser(user.userId, {
2831
onSuccess: () => {
2932
onClose();
33+
clearAccessToken();
34+
navigate("/login");
3035
}
3136
});
3237
};

frontend/src/shared/components/UserDashboard/UserProfileDetails.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ const UserProfileDetails = () => {
2727
navigate("/login");
2828
};
2929

30-
const handleDeleteAccount = () => {
31-
console.log("Delete account clicked");
32-
setShowSettingsDialog(false);
33-
};
34-
3530
const handleUpdateEmail = () => {
3631
setShowSettingsDialog(false);
3732
setShowEmailDialog(true);

0 commit comments

Comments
 (0)