-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fogetpassword * ft-forget password * reset and forget password * reset and forget password * reset and forget password * reset and forget password * reset password * reset update background * reset update background * reset update background * reset password * reset password * reset password * reset password * yarn.lock * Ft search job post (#187) * ft(Add search): Add entry search * Will add user seach Job Functionalities --squashed commits * single page to view and search Jobs * will make jobs and search be on same page * updating functionality * Real time searching --------- Co-authored-by: Jmukakalisa <[email protected]> * 154 crud application form (#167) * Will add create application feature * Will add update application form feature * Will add view application form feature * Will add dark mode card * Will add create application feature * Will add update application form feature * Will add view application form feature * Will add dark mode card * Add Verify Email (#179) Co-authored-by: vegetason <[email protected]> * resolve comflit * resolve comflit * resolve comflit * resolve comflit --------- Co-authored-by: Aime Brues <[email protected]> Co-authored-by: Jmukakalisa <[email protected]> Co-authored-by: MUGISHA Joseph <[email protected]> Co-authored-by: Irakoze Paulin <[email protected]> Co-authored-by: vegetason <[email protected]>
- Loading branch information
Showing
14 changed files
with
20,527 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,5 @@ buildcoverage | |
package-lock.json | ||
yarn.lock | ||
.DS_Store | ||
build/ | ||
build/ | ||
yarn.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React from "react"; | ||
import { Link } from "react-router-dom"; | ||
import { Formik, Field, Form, ErrorMessage } from 'formik'; | ||
import { validationSchema } from "../validation/forgetvalidation"; | ||
|
||
interface ForgotPasswordFormProps { | ||
emailSent: boolean; | ||
error: string; | ||
onSubmit: (values: { email: string }, { setSubmitting }) => Promise<void>; | ||
} | ||
|
||
export const ForgotPasswordForm: React.FC<ForgotPasswordFormProps> = ({ emailSent, error, onSubmit }) => { | ||
if (emailSent) { | ||
return ( | ||
<p className="text-white dark:text-green-400 text-center"> | ||
An email with password reset instructions has been sent to your email address. | ||
</p> | ||
); | ||
} | ||
|
||
return ( | ||
<> | ||
<Formik | ||
initialValues={{ email: '' }} | ||
validationSchema={validationSchema} | ||
onSubmit={onSubmit} | ||
> | ||
{({ isSubmitting }) => ( | ||
<Form className="w-full max-w-md px-4"> | ||
<div className="mb-4"> | ||
<Field | ||
name="email" | ||
type="email" | ||
className="w-full rounded-md px-2 py-3 border border-white placeholder:text-gray-400 text-black dark:text-white sm:text-[12px] outline-none bg-gray-100 dark:bg-[#1F2A37]" | ||
placeholder="Enter your email" | ||
/> | ||
<ErrorMessage | ||
name="email" | ||
component="div" | ||
className="text-red-500 text-xs mt-1" | ||
/> | ||
</div> | ||
|
||
{error && ( | ||
<div className="text-red-500 text-sm mb-4 text-center"> | ||
{error} | ||
</div> | ||
)} | ||
|
||
<button | ||
type="submit" | ||
disabled={isSubmitting} | ||
className="w-full py-2 px-4 text-white rounded-md bg-[#56C870] focus:outline-none disabled:opacity-50" | ||
> | ||
{isSubmitting ? 'Submitting...' : 'Send Reset Link'} | ||
</button> | ||
</Form> | ||
)} | ||
</Formik> | ||
|
||
<p className="mt-4 text-sm dark:text-white text-center"> | ||
Remembered your password?{" "} | ||
<Link to="/login" className="text-blue-600 dark:text-blue-400"> | ||
Login here | ||
</Link> | ||
</p> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React from "react"; | ||
import { Formik, Field, Form, ErrorMessage } from "formik"; | ||
import { validationSchema } from "../validation/resetvalidation"; | ||
|
||
interface ResetPasswordFormProps { | ||
onSubmit: (values: { password: string }, { setSubmitting }) => Promise<void>; | ||
error: string; | ||
} | ||
|
||
export const ResetPasswordForm: React.FC<ResetPasswordFormProps> = ({ onSubmit, error }) => ( | ||
<Formik | ||
initialValues={{ password: "", confirmPassword: "" }} | ||
validationSchema={validationSchema} | ||
onSubmit={onSubmit} | ||
> | ||
{({ isSubmitting }) => ( | ||
<Form className="w-full max-w-md"> | ||
<PasswordField name="password" placeholder="Enter your new password" /> | ||
<PasswordField name="confirmPassword" placeholder="Confirm your new password" /> | ||
{error && ( | ||
<div className="mb-4 text-red-500 text-sm text-center"> | ||
{error} | ||
</div> | ||
)} | ||
<SubmitButton isSubmitting={isSubmitting} /> | ||
</Form> | ||
)} | ||
</Formik> | ||
); | ||
|
||
const PasswordField: React.FC<{ name: string; placeholder: string }> = ({ name, placeholder }) => ( | ||
<div className="mb-4"> | ||
<Field | ||
name={name} | ||
type="password" | ||
className="w-full rounded-md px-2 py-3 border border-white placeholder:text-gray-400 text-black dark:text-white sm:text-[12px] outline-none bg-gray-100 dark:bg-[#1F2A37]" | ||
placeholder={placeholder} | ||
/> | ||
<ErrorMessage | ||
name={name} | ||
component="div" | ||
className="text-red-500 text-xs mt-1" | ||
/> | ||
</div> | ||
); | ||
|
||
const SubmitButton: React.FC<{ isSubmitting: boolean }> = ({ isSubmitting }) => ( | ||
<button | ||
type="submit" | ||
disabled={isSubmitting} | ||
className="w-full py-2 px-4 text-white rounded-md bg-[#56C870] focus:outline-none disabled:opacity-50" | ||
> | ||
{isSubmitting ? "Submitting..." : "New Password"} | ||
</button> | ||
); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import React, { useEffect } from "react"; | ||
import { useNavigate } from "react-router-dom"; | ||
|
||
interface SuccessMessageProps { | ||
message: string; | ||
onNavigate: () => void; | ||
} | ||
|
||
export const SuccessMessage: React.FC<SuccessMessageProps> = ({ message, onNavigate }) => { | ||
const navigate = useNavigate(); | ||
|
||
useEffect(() => { | ||
const timer = setTimeout(() => { | ||
onNavigate(); | ||
}, 3000); | ||
|
||
return () => clearTimeout(timer); | ||
}, [onNavigate]); | ||
|
||
return ( | ||
<div className="text-center"> | ||
<p className="text-xl font-bold text-green-600 dark:text-gray-300">{message}</p> | ||
<p className="mt-2 text-gray-200 dark:text-gray-300"> | ||
Redirecting to login in 3 seconds... | ||
</p> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { useState } from "react"; | ||
import { request } from "graphql-request"; | ||
|
||
const RESET_PASSWORD_MUTATION = ` | ||
mutation ForgetPassword($email: String!) { | ||
forgetPassword(email: $email) | ||
} | ||
`; | ||
|
||
export const useForgotPassword = () => { | ||
const [emailSent, setEmailSent] = useState(false); | ||
const [error, setError] = useState(""); | ||
|
||
const handleSubmit = async (values: { email: string }, { setSubmitting }) => { | ||
try { | ||
const API_URL = process.env.BACKEND_URL; | ||
await request(`${API_URL}/graphql`, RESET_PASSWORD_MUTATION, { | ||
email: values.email, | ||
}); | ||
setEmailSent(true); | ||
setError(""); | ||
} catch (error: any) { | ||
setError(error.response?.errors?.[0]?.message || "An error occurred. Please try again."); | ||
} finally { | ||
setSubmitting(false); | ||
} | ||
}; | ||
|
||
return { emailSent, error, handleSubmit }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { useState } from "react"; | ||
import { request, ClientError } from "graphql-request"; | ||
|
||
interface ResetPasswordResponse { | ||
resetPassword: string | ||
} | ||
|
||
interface ResetPasswordValues { | ||
password: string; | ||
} | ||
|
||
interface SubmittingHelpers { | ||
setSubmitting: (isSubmitting: boolean) => void; | ||
} | ||
|
||
interface ResetState { | ||
successMessage: string; | ||
error: string; | ||
} | ||
|
||
const RESET_PASSWORD_MUTATION = ` | ||
mutation ResetPassword($token: String!, $newPassword: String!) { | ||
resetPassword(token: $token, newPassword: $newPassword) | ||
} | ||
`; | ||
|
||
const extractSuccessMessage = (error: ClientError): string | null => { | ||
const match = error.response.errors?.[0]?.message.match(/message: "(.*?)"/); | ||
return match?.[1] || null; | ||
}; | ||
|
||
const getErrorMessage = (error: unknown): string => { | ||
if (error instanceof ClientError) { | ||
return error.response.errors?.[0]?.message || "An error occurred while resetting your password."; | ||
} | ||
if (error instanceof Error) { | ||
return error.message; | ||
} | ||
return "An unexpected error occurred"; | ||
}; | ||
|
||
const processResponse = (response: ResetPasswordResponse): ResetState => ({ | ||
successMessage: response.resetPassword || "", | ||
error: "", | ||
}); | ||
|
||
const processError = (error: unknown): ResetState => { | ||
if (error instanceof ClientError) { | ||
const successMessage = extractSuccessMessage(error); | ||
if (successMessage) { | ||
return { successMessage, error: "" }; | ||
} | ||
} | ||
return { successMessage: "", error: getErrorMessage(error) }; | ||
}; | ||
|
||
const makeRequest = async (url: string, token: string, password: string): Promise<ResetPasswordResponse> => { | ||
return request<ResetPasswordResponse>( | ||
url, | ||
RESET_PASSWORD_MUTATION, | ||
{ | ||
token, | ||
newPassword: password, | ||
} | ||
); | ||
}; | ||
|
||
const usePasswordResetRequest = () => { | ||
const sendRequest = async (token: string, password: string): Promise<ResetState> => { | ||
const API_URL = process.env.BACKEND_URL; | ||
if (!API_URL) { | ||
return { successMessage: "", error: "Backend URL is not defined" }; | ||
} | ||
|
||
try { | ||
const response = await makeRequest(`${API_URL}/graphql`, token, password); | ||
return processResponse(response); | ||
} catch (error) { | ||
return processError(error); | ||
} | ||
}; | ||
|
||
return { sendRequest }; | ||
}; | ||
|
||
export const useResetPassword = (token: string | null) => { | ||
const [successMessage, setSuccessMessage] = useState(""); | ||
const [error, setError] = useState(""); | ||
const { sendRequest } = usePasswordResetRequest(); | ||
|
||
const handleSubmit = async ( | ||
values: ResetPasswordValues, | ||
{ setSubmitting }: SubmittingHelpers | ||
) => { | ||
if (!token) { | ||
setSuccessMessage(""); | ||
setError("Invalid or missing token"); | ||
setSubmitting(false); | ||
return; | ||
} | ||
|
||
try { | ||
const result = await sendRequest(token, values.password); | ||
setSuccessMessage(result.successMessage); | ||
setError(result.error); | ||
} finally { | ||
setSubmitting(false); | ||
} | ||
}; | ||
|
||
return { successMessage, error, handleSubmit }; | ||
}; |
Oops, something went wrong.