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
6 changes: 3 additions & 3 deletions app/(Auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ declare global {
render: (container: string, options: { sitekey: string, callback: (token: string) => void }) => void;
};
onTurnstileSuccess: (token: string) => void;
__turnstileRendered: boolean;
}
}

Expand All @@ -36,6 +35,7 @@ const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const tokenRef = useRef('');
const widgetRendered = useRef(false);

useEffect(() => {
const existingScript = document.querySelector('script[src="https://challenges.cloudflare.com/turnstile/v0/api.js"]');
Expand Down Expand Up @@ -115,8 +115,8 @@ const Login = () => {
};

useEffect(() => {
if (window.__turnstileRendered) return;
window.__turnstileRendered = true;
if (widgetRendered.current) return;
widgetRendered.current = true;

const renderTurnstile = () => {
if (!window.turnstile) return;
Expand Down
75 changes: 70 additions & 5 deletions app/(Auth)/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client"
import { useState } from 'react';
import { useState, useRef, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { signIn } from "next-auth/react";
import Link from 'next/link';
Expand All @@ -16,13 +16,68 @@ import {
} from '@/components/ui/card';
import Banner from '@/components/Banner';

declare global {
interface Window {
turnstile: {
render: (container: string, options: { sitekey: string, callback: (token: string) => void }) => void;
};
onTurnstileSuccess: (token: string) => void;
}
}


const SignIn = () => {
const router = useRouter();
const searchParams = useSearchParams()
const [Name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, seterror] = useState("")
const tokenRef = useRef('');
const widgetRendered = useRef(false);

useEffect(() => {
const existingScript = document.querySelector('script[src="https://challenges.cloudflare.com/turnstile/v0/api.js"]');

if (!existingScript) {
const script = document.createElement("script");
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
script.async = true;
document.body.appendChild(script);
}

window.onTurnstileSuccess = (token: string) => {
tokenRef.current = token;
};

return () => {
delete window.onTurnstileSuccess;
};
}, []);

useEffect(() => {
if (widgetRendered.current) return;
widgetRendered.current = true;

const renderTurnstile = () => {
if (!window.turnstile) return;

window.turnstile.render("#turnstile-container", {
sitekey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!,
callback: (token) => (tokenRef.current = token),
});
};

if (!window.turnstile) {
const script = document.createElement("script");
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
script.async = true;
script.onload = renderTurnstile;
document.body.appendChild(script);
} else {
renderTurnstile();
}
}, []);

const role = searchParams.get('role')

Expand Down Expand Up @@ -50,6 +105,8 @@ const SignIn = () => {
}

try {
console.log("Turnstile Token before /api/signin:", tokenRef.current);

const res = await fetch('/api/signin', {
method: "POST",
headers: {
Expand All @@ -72,20 +129,25 @@ const SignIn = () => {
else if (res.status === 200) {
seterror("")

console.log("Turnstile token passed to signIn:", tokenRef.current);
const loginResult = await signIn("credentials", {
redirect: false,
email,
password,
'cf-turnstile-response': tokenRef.current,
});

if (loginResult.ok) {
console.log("loginResult:", loginResult);

if (loginResult?.ok) {
if (role === "Client") {
router.replace("/Cdash");
router.replace("/Cprofile/completeProfile");
} else {
router.replace("/Fdash");
router.replace("/Fprofile/completeProfile");
}
} else {
router.push("/login");
console.error("signIn error:", loginResult?.error);
seterror(loginResult?.error || 'Authentication failed after sign-in');
}
}
else {
Expand Down Expand Up @@ -148,6 +210,9 @@ const SignIn = () => {
required
/>
</div>

<div id="turnstile-container" className="my-4"></div>

{error && <p className='text-destructive'>{error}</p>}
<Button
type='submit'
Expand Down
123 changes: 123 additions & 0 deletions app/(Cinterface)/Cprofile/completeProfile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"use client"

import { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
import { User, CprofileType } from '@/types/User';
import { toast } from 'sonner';
import { Spinner } from '@/components/ui/spinner';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import { Field, FieldGroup } from '@/components/ui/field';
import Banner from '@/components/Banner';
import { useRouter } from 'next/navigation';

const CompleteProfile = () => {
const { data: session } = useSession();
const [clientProfile, setClientProfile] = useState<CprofileType | null>(null);
const [image, setImage] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const [loading, setLoading] = useState(false);

const router = useRouter();

const handleSubmit = async () => {
setLoading(true);

const formData = new FormData();
formData.append("userId", session.user.id);
formData.append("companyName", clientProfile.companyName || "");
formData.append("bio", clientProfile.bio || "")
formData.append("description", clientProfile.description || "");
formData.append("location", clientProfile.location || "");
formData.append("website", clientProfile.website || "");
if (image) {
formData.append("logo", image);
}

const response = await fetch("/api/profile/ClientProfile", {
method: "POST",
body: formData
});
const data = await response.json();
if (response.ok) {
toast.success("Profile updated successfully");
router.replace("/Cdash");
} else {
toast.error(data.message || "Failed to update profile");
}
setLoading(false);
}

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];

if (!file) {
toast.error("No file selected")
return;
}

// Validate file type
if (!file.type.startsWith("image/")) {
toast.error("Please select a valid image file.");
return;
}

setImage(file);
setPreview(URL.createObjectURL(file)); // Create preview URL
};

if (loading) {
return (
<div className="flex justify-center items-center h-screen bg-white dark:bg-slate-900">
<Spinner className="size-10" />
</div>
);
}

return (
<div className='bg-linear-to-b from-slate-50 to-slate-100 dark:from-slate-950 dark:to-slate-900'>
<Banner />

<div className='flex justify-center'>
<FieldGroup className='w-full max-w-md m-5 border shadow-lg p-6 rounded-xl'>
<h1 className='text-center text-2xl font-bold mb-4'>Complete Your Profile</h1>
<Field>
<Label htmlFor='companyName'>Company Name</Label>
<Input id='companyName' type="text" placeholder='Company Name' value={clientProfile?.companyName || ""} onChange={(e) => setClientProfile({ ...clientProfile, companyName: e.target.value })} />

<Label htmlFor='bio'>Bio</Label>
<Input id='bio' type="text" placeholder='Bio' value={clientProfile?.bio || ""} onChange={(e) => setClientProfile({ ...clientProfile, bio: e.target.value })} />

<Label htmlFor='location'>Location</Label>
<Input id='location' type="text" placeholder='Location' value={clientProfile?.location || ""} onChange={(e) => setClientProfile({ ...clientProfile, location: e.target.value })} />

<Label htmlFor='website'>Website</Label>
<Input id='website' type="text" placeholder='Website' value={clientProfile?.website || ""} onChange={(e) => setClientProfile({ ...clientProfile, website: e.target.value })} />

<Label htmlFor='about'>About</Label>
<Textarea id='about' placeholder='About' value={clientProfile?.description || ""} onChange={(e) => setClientProfile({ ...clientProfile, description: e.target.value })} />

<Label htmlFor="logo">Profile Picture</Label>
<Input id="logo" name="logo"
type="file"
accept="image/*"
onChange={(e) => handleFileChange(e)} />
{preview && (
<img
src={preview || undefined}
alt="Preview"
className="mt-2 w-24 h-24 object-cover rounded"
/>
)}

<Button type="button" onClick={handleSubmit}>Complete Profile</Button>
</Field>
</FieldGroup>
</div>
</div>
)
}

export default CompleteProfile
8 changes: 4 additions & 4 deletions app/(Cinterface)/Cprofile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Cprofile = () => {
})
.then(res => res.json())
.then(data => {
//console.log(data)
console.log(data)
const user: User = data.user
const clientProfile: CprofileType = data.profile;

Expand Down Expand Up @@ -208,13 +208,13 @@ const Cprofile = () => {
<div className="relative overflow-hidden rounded-2xl bg-linear-to-br from-blue-500 to-blue-600 p-8 shadow-xl transform hover:scale-105 transition-all duration-300">
<div className="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -mr-16 -mt-16"></div>
<h3 className="text-sm font-semibold uppercase tracking-wide mb-2">Member Since</h3>
<p className="text-3xl font-bold">2024</p>
<p className="text-3xl font-bold">{user?.createdAt || "N/A"}</p>
</div>

<div className="relative overflow-hidden rounded-2xl bg-linear-to-br from-yellow to-light_yellow p-8 shadow-xl transform hover:scale-105 transition-all duration-300">
<div className="absolute top-0 right-0 w-32 h-32 bg-white/10 rounded-full -mr-16 -mt-16"></div>
<h3 className="text-sm font-semibold uppercase tracking-wide mb-2">Active Jobs</h3>
<p className="text-deep_blue text-3xl font-bold">0</p>
<h3 className="text-sm font-semibold uppercase tracking-wide mb-2">Posted Jobs</h3>
<p className="text-deep_blue text-3xl font-bold">{clientProfile?.postedJobs?.length || 0}</p>
</div>

<div className="relative overflow-hidden rounded-2xl bg-linear-to-br from-indigo-500 to-indigo-600 p-8 shadow-xl transform hover:scale-105 transition-all duration-300">
Expand Down
Loading
Loading