Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions certs/server.cert

This file was deleted.

28 changes: 0 additions & 28 deletions certs/server.key

This file was deleted.

3 changes: 1 addition & 2 deletions src/components/route-protection/auth-protected-route.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// auth-protected-route.tsx
import React from "react";
import { ProtectedRoute } from "@/components/route-protection/protected-route";
import { ProtectedRoute } from "./protected-route";

interface AuthProtectedRouteProps {
children: React.ReactNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import React from "react";
import { ProtectedRoute } from "@/components/route-protection/protected-route";
import { ProtectedRoute } from "./protected-route";

interface DashboardProtectedRouteProps {
children: React.ReactNode;
Expand Down
44 changes: 37 additions & 7 deletions src/components/route-protection/protected-route.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { useProtectedRoute } from "@/hooks/use-protected-route";
import React, { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { useAuth } from "@/hooks/useAuth";
import { BookOpen } from "lucide-react";

interface ProtectedRouteProps {
Expand All @@ -11,19 +12,48 @@ export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
children,
requireVerification = true,
}) => {
const { loading, shouldRender } = useProtectedRoute(requireVerification);
const { loading, isUserExist, isVerified } = useAuth();
const navigate = useNavigate();
const hasRedirected = useRef(false);

useEffect(() => {
if (loading || hasRedirected.current) return;

// -------- First-time visitor --------
if (!isUserExist) {
navigate("/auth/sign-up", { replace: true });
hasRedirected.current = true;
return;
}

// -------- Unverified users --------
if (requireVerification && !isVerified) {
if (!window.location.pathname.startsWith("/auth/user-email")) {
navigate("/auth/user-email", { replace: true });
hasRedirected.current = true;
}
return;
}

// -------- Verified users trying to access auth pages --------
const authPages = ["/auth/sign-in", "/auth/sign-up"];
if (isVerified && authPages.includes(window.location.pathname)) {
navigate("/", { replace: true });
hasRedirected.current = true;
}
}, [loading, isUserExist, isVerified, requireVerification, navigate]);

// -------- Loading state --------
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg ">
<BookOpen className="size-60 animate-pulse" />
</div>
<BookOpen className="size-60 animate-pulse" />
</div>
);
}

if (!shouldRender) {
// -------- Block rendering for unverified users on dashboard --------
if (requireVerification && !isVerified) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Please verify your account</div>
Expand Down
16 changes: 12 additions & 4 deletions src/components/service-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@ import { ArrowRight } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Link } from "react-router-dom";

interface ServiceCardProps {
icon: React.ComponentType<{ className?: string }>;
title: string;
description: string;
buttonText: string;
linkText: string;
buttonVariant?: "default" | "outline" | "secondary";
className?: string;
link: string;
}

export const ServiceCard = ({
icon: Icon,
title,
description,
buttonText,
linkText,
buttonVariant = "outline",
className,
link,
}: ServiceCardProps) => {
return (
<Card
Expand All @@ -42,8 +45,13 @@ export const ServiceCard = ({
<p className="text-sm text-muted-foreground leading-relaxed">
{description}
</p>
<Button variant={buttonVariant} size="sm" className="w-full mt-4">
{buttonText}
<Button
variant={buttonVariant}
size="sm"
className="w-full mt-4"
asChild
>
<Link to={link}>{linkText}</Link>
</Button>
</div>
</CardContent>
Expand Down
52 changes: 36 additions & 16 deletions src/components/setting-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Card, CardContent } from "@/components/ui/card";
import { Camera, User } from "lucide-react";
import { Camera } from "lucide-react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
Expand All @@ -16,6 +16,7 @@ import { student } from "@/store/atoms/user";
const base_api = import.meta.env.VITE_SERVER_BASE_URL;

interface UserData {
profilepic: File | null;
firstname: string;
lastname: string;
email: string;
Expand All @@ -30,6 +31,7 @@ const SettingProfile = () => {
const refreshAdmin = useRecoilRefresher_UNSTABLE(student);

const [userData, setUserData] = useState<UserData>({
profilepic: null,
firstname: "",
lastname: "",
email: "",
Expand All @@ -40,42 +42,51 @@ const SettingProfile = () => {

// form handler

// form submit handler
const handleFormSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);

// removing empty fields
const filteredData: Partial<UserData> = Object.fromEntries(
Object.entries(userData).filter(([_, value]) => value !== "")
);

try {
const formData = new FormData();
Object.entries(userData).forEach(([key, value]) => {
if (value) {
formData.append(key, value);
}
});

const response = await axios.put(
`${base_api}/api/v1/update-profile`,
filteredData,
{ withCredentials: true }
formData,
{
withCredentials: true,
headers: { "Content-Type": "multipart/form-data" },
}
);

if (response.status === 200) {
setUserData({
profilepic: null,
firstname: "",
lastname: "",
email: "",
address: "",
bio: "",
dob: "",
});
toast.success("Profile updated successfully");
setIsLoading(false);
toast.success("Profile updated successfully");
refreshAdmin();
}
} catch (error) {
console.log(error);
setIsLoading(false);
toast.error("Failed to update profile");
} finally {
setIsLoading(false);
}
};


if (loading) {
return (
<Card>
Expand Down Expand Up @@ -120,25 +131,34 @@ const SettingProfile = () => {
<CardContent className="p-6 space-y-6">
{/* Profile Photo */}
<div className="flex flex-col md:flex-row items-center gap-4">
<div className="w-20 h-20 bg-secondary rounded-full flex items-center justify-center">
{loading ? (
<User className="size-7" />
<div className="w-20 h-20 bg-secondary rounded-full flex items-center justify-center overflow-hidden">
{userData.profilepic ? (
<img
src={URL.createObjectURL(userData.profilepic)}
alt="Preview"
className="h-16 w-16 rounded-full object-cover"
/>
) : (
<img
src={studentInfo?.profilepic || "./user.svg"}
className="size-12 rounded-full dark:text-secondary"
className="h-16 w-16 rounded-full object-cover"
/>
)}
</div>
<label className="flex items-center gap-2 cursor-pointer">
<Input type="file" className="hidden" />
<Input
type="file"
className="hidden"
name="profilepic"
onChange={(e) => InputHandler(e, setUserData)}
/>
<span className="px-4 py-2 bg-secondary text-sm rounded-md">
Change Photo
</span>
<Camera className="h-4 w-4" />
</label>
<p className="text-sm text-muted-foreground">
JPG, PNG or GIF. Max size 2MB.
JPG or PNG, Max size 2MB.
</p>
</div>

Expand Down
41 changes: 0 additions & 41 deletions src/hooks/use-protected-route.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/hooks/use-route-guard.ts

This file was deleted.

20 changes: 20 additions & 0 deletions src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useState, useEffect } from "react";
import { useUserData } from "@/store/hooks/useUserData";

export const useAuth = () => {
const { studentInfo, loading } = useUserData();
const [checkingSession, setCheckingSession] = useState(true);

useEffect(() => {
if (!loading) {
setCheckingSession(false);
}
}, [loading]);

return {
studentInfo,
loading: loading || checkingSession,
isUserExist: !!studentInfo,
isVerified: studentInfo?.isVerified || false,
};
};
Loading