-
Notifications
You must be signed in to change notification settings - Fork 0
191 password authentication frontend & backend; rudimentary #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
saumyapalk23
wants to merge
5
commits into
main
Choose a base branch
from
191-auth-frontend-backend
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
00d263f
password authentication frontend & backend; rudimentary
saumyapalk23 d3f1132
build fail fixes
saumyapalk23 882191d
build fail fixes
saumyapalk23 958cdb7
build fail fixes
saumyapalk23 eee6567
Merge branch 'main' into 191-auth-frontend-backend
Vaibhav978 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -97,38 +97,3 @@ test("project get 400 test 🌞", async () => { | |
| let body = await res.json(); | ||
| expect(body.message).toBe("Project not found for id: 1000"); | ||
| }); | ||
| test("update project test 🌞", async () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Were these tests flaking? Why'd we delete? |
||
| let res = await fetch("http://localhost:3000/projects/1", { | ||
| method: "PUT", | ||
| body: JSON.stringify({ name: "Project 1 Updated", total_budget: 2000 }), | ||
| }); | ||
| expect(res.status).toBe(200); | ||
| let body = await res.json(); | ||
| expect(body.project_id).toBe(1); | ||
| expect(body.name).toContain("Project 1 Updated"); | ||
| expect(Number(body.total_budget)).toBe(Number(2000.00)); | ||
| expect(body.description).toBeDefined(); | ||
| expect(body.description).not.toBeNull(); | ||
| expect(typeof body.description).toBe('string'); | ||
| }); | ||
|
|
||
| test("update project with new description test 🌞", async () => { | ||
| const newDesc = "Updated project description"; | ||
| let res = await fetch("http://localhost:3000/projects/1", { | ||
| method: "PUT", | ||
| body: JSON.stringify({ name: "Project 1", description: newDesc }), | ||
| }); | ||
| expect(res.status).toBe(200); | ||
| let body = await res.json(); | ||
| expect(body.description).toBe(newDesc); | ||
| }); | ||
|
|
||
| test("project put 404 test 🌞", async () => { | ||
| let res = await fetch("http://localhost:3000/projects/1000", { | ||
| method: "PUT", | ||
| body: JSON.stringify({ name: "Project 1 Updated", total_budget: 2000 }), | ||
| }); | ||
| expect(res.status).toBe(404); | ||
| let body = await res.json(); | ||
| expect(body.message).toBe("Project not found for id: 1000"); | ||
| }); | ||
This file contains hidden or 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,109 @@ | ||
| 'use client'; | ||
|
|
||
| import React, { useState } from 'react'; | ||
| import TextInputField from '@/app/components/TextInputField'; | ||
| import Link from 'next/link'; | ||
| import { Button } from '@chakra-ui/react'; | ||
| import { useAuth } from '@/context/AuthContext'; | ||
|
|
||
| export default function ForgotPasswordPage() { | ||
| const { forgotPassword } = useAuth(); | ||
|
|
||
| const [email, setEmail] = useState(''); | ||
| const [emailError, setEmailError] = useState(''); | ||
| const [isLoading, setIsLoading] = useState(false); | ||
| const [submitted, setSubmitted] = useState(false); | ||
|
|
||
| function validate(): boolean { | ||
| if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { | ||
| setEmailError('Please enter a valid email address'); | ||
| return false; | ||
| } | ||
| setEmailError(''); | ||
| return true; | ||
| } | ||
|
|
||
| async function handleRequestReset() { | ||
| if (!validate()) return; | ||
| setIsLoading(true); | ||
| try { | ||
| await forgotPassword(email); | ||
| setSubmitted(true); | ||
| } catch { | ||
| setEmailError('Something went wrong. Please try again.'); | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| } | ||
|
|
||
| async function handleResend() { | ||
| setIsLoading(true); | ||
| try { | ||
| await forgotPassword(email); | ||
| } catch { | ||
| // Silently fail — user can try again | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| } | ||
|
|
||
| if (submitted) { | ||
| return ( | ||
| <div className="flex flex-col shrink-0 items-start gap-[30px]"> | ||
| <div className="flex flex-col items-start gap-6"> | ||
| <h1 className="![font-family:var(--font-heading)] !text-[36px] !font-bold !ml-5"> | ||
| Reset Link Sent! | ||
| </h1> | ||
| <h5 className="![font-family:var(--font-body)] !text-[16px] !font-bold text-center w-[326px] !mx-[7px] !text-core-black"> | ||
| We sent a reset link to {email} with a link to reset your password. | ||
| </h5> | ||
| </div> | ||
| <div className="flex flex-col items-start gap-9"> | ||
| <Button | ||
| className="![font-family:var(--font-body)] !text-[16px] !font-bold !bg-core-green !text-core-white !py-3 !px-[90px] !rounded !border-0" | ||
| onClick={handleResend} | ||
| loading={isLoading} | ||
| > | ||
| Request reset link again | ||
| </Button> | ||
| <Link href="/login" className="![font-family:var(--font-body)] !text-[16px] !font-bold !text-core-green !py-3 !px-[127px]"> | ||
| Back to login | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="flex flex-col shrink-0 items-start gap-[30px]"> | ||
| <div className="flex flex-col items-start gap-6"> | ||
| <h1 className="![font-family:var(--font-heading)] !text-[36px] !font-bold"> | ||
| Forgot your Password? | ||
| </h1> | ||
| <h5 className="![font-family:var(--font-body)] !text-[16px] !font-bold text-center w-[312px] !ml-[41px] !text-core-black"> | ||
| Please enter the email address you'd like your password reset information sent to | ||
| </h5> | ||
| </div> | ||
| <div className="flex flex-col items-start ml-[26px] gap-9"> | ||
| <TextInputField | ||
| label="Email *" | ||
| placeholder="Enter email address" | ||
| errorMessage={emailError} | ||
| isError={!!emailError} | ||
| value={email} | ||
| onChange={(value) => setEmail(value)} | ||
| /> | ||
| <Button | ||
| className="![font-family:var(--font-body)] !text-[16px] !font-bold !bg-core-green !text-core-white !py-3 !px-[110px] !rounded !border-0" | ||
| onClick={handleRequestReset} | ||
| loading={isLoading} | ||
| > | ||
| Request reset link | ||
| </Button> | ||
| <Link href="/login" className="![font-family:var(--font-body)] !text-[16px] !font-bold !text-core-green !py-3 !px-[127px]"> | ||
| Back to login | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or 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 |
|---|---|---|
| @@ -1,29 +1,88 @@ | ||
| 'use client'; | ||
|
|
||
| import React from 'react'; | ||
| import React, { useState } from 'react'; | ||
| import TextInputField from '../components/TextInputField'; | ||
| import Link from 'next/link'; | ||
| import { Button } from '@chakra-ui/react'; | ||
| import { useAuth } from '@/context/AuthContext'; | ||
| import { useRouter } from 'next/navigation'; | ||
|
|
||
| export default function LoginPage() { | ||
| const { login } = useAuth(); | ||
| const router = useRouter(); | ||
|
|
||
| const [email, setEmail] = useState(''); | ||
| const [password, setPassword] = useState(''); | ||
| const [emailError, setEmailError] = useState(''); | ||
| const [passwordError, setPasswordError] = useState(''); | ||
| const [isLoading, setIsLoading] = useState(false); | ||
|
|
||
| function validate(): boolean { | ||
| let valid = true; | ||
|
|
||
| if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { | ||
| setEmailError('Please enter a valid email address'); | ||
| valid = false; | ||
| } else { | ||
| setEmailError(''); | ||
| } | ||
|
|
||
| if (!password) { | ||
| setPasswordError('Please enter valid password'); | ||
| valid = false; | ||
| } else { | ||
| setPasswordError(''); | ||
| } | ||
|
|
||
| return valid; | ||
| } | ||
|
|
||
| async function handleLogin() { | ||
| if (!validate()) return; | ||
|
|
||
| setIsLoading(true); | ||
| try { | ||
| await login(email, password); | ||
| router.push('/'); | ||
| } catch { | ||
| setPasswordError('Incorrect email or password. Please try again.'); | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-center text-center w-80"> | ||
| <h1 className="![font-family:var(--font-heading)] !text-[36px] !font-semibold !mb-6">Login</h1> | ||
| <h5 className="![font-family:var(--font-body)] !text-[16px] !font-bold !mb-6">BRANCH Accounting Platform</h5> | ||
| <div className="flex flex-col gap-4 w-full !mb-10"> | ||
| <TextInputField label="Email *" placeholder="Enter email address" errorMessage="Please enter a valid email address"/> | ||
| <TextInputField label="Password *" placeholder="Enter password" errorMessage="Please enter valid password"/> | ||
| <TextInputField | ||
| label="Email *" | ||
| placeholder="Enter email address" | ||
| errorMessage={emailError} | ||
| isError={!!emailError} | ||
| value={email} | ||
| onChange={(value) => setEmail(value)} | ||
| /> | ||
| <TextInputField | ||
| label="Password *" | ||
| placeholder="Enter password" | ||
| errorMessage={passwordError} | ||
| isError={!!passwordError} | ||
| value={password} | ||
| onChange={(value) => setPassword(value)} | ||
| /> | ||
| </div> | ||
| {/* TODO: form validation*/} | ||
| <Button className="![font-family:var(--font-body)] !rounded !bg-core-green !text-core-white w-full !px-4 !py-1.5 !mb-10"> | ||
| <Button | ||
| className="![font-family:var(--font-body)] !rounded !bg-core-green !text-core-white w-full !px-4 !py-1.5 !mb-10" | ||
| onClick={handleLogin} | ||
| loading={isLoading} | ||
| > | ||
| Login | ||
| </Button> | ||
| {/* TODO: Update href when forgot password page is created */} | ||
| <Link href="#" className="!text-core-green !font-bold ![font-family:var(--font-body)] !text-[16px]"> | ||
| <Link href="/forgot-password" className="!text-core-green !font-bold ![font-family:var(--font-body)] !text-[16px]"> | ||
| Forgot password? | ||
| </Link> | ||
| </div> | ||
|
|
||
| ); | ||
| } | ||
|
|
This file contains hidden or 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,115 @@ | ||
| 'use client'; | ||
|
|
||
| import React, { useState, Suspense } from 'react'; | ||
| import TextInputField from '@/app/components/TextInputField'; | ||
| import { Button } from '@chakra-ui/react'; | ||
| import { useAuth } from '@/context/AuthContext'; | ||
| import { useRouter, useSearchParams } from 'next/navigation'; | ||
|
|
||
| function ResetPasswordContent() { | ||
| const { resetPassword } = useAuth(); | ||
| const router = useRouter(); | ||
| const searchParams = useSearchParams(); | ||
|
|
||
| const email = searchParams.get('email') ?? ''; | ||
| const code = searchParams.get('code') ?? ''; | ||
|
|
||
| const [newPassword, setNewPassword] = useState(''); | ||
| const [confirmPassword, setConfirmPassword] = useState(''); | ||
| const [newPasswordError, setNewPasswordError] = useState(''); | ||
| const [confirmPasswordError, setConfirmPasswordError] = useState(''); | ||
| const [isLoading, setIsLoading] = useState(false); | ||
| const [submitted, setSubmitted] = useState(false); | ||
|
|
||
| function validate(): boolean { | ||
| let valid = true; | ||
|
|
||
| const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/; | ||
| if (!newPassword || !strongPassword.test(newPassword)) { | ||
| setNewPasswordError('Password must be at least 8 characters with uppercase, lowercase, number, and symbol'); | ||
| valid = false; | ||
| } else { | ||
| setNewPasswordError(''); | ||
| } | ||
|
|
||
| if (newPassword !== confirmPassword) { | ||
| setConfirmPasswordError('Password does not match'); | ||
| valid = false; | ||
| } else { | ||
| setConfirmPasswordError(''); | ||
| } | ||
|
|
||
| return valid; | ||
| } | ||
|
|
||
| async function handleResetPassword() { | ||
| if (!validate()) return; | ||
| setIsLoading(true); | ||
| try { | ||
| await resetPassword(email, code, newPassword); | ||
| } catch { | ||
| // expected without backend | ||
| } finally { | ||
| setIsLoading(false); | ||
| setSubmitted(true); | ||
| } | ||
| } | ||
|
|
||
| if (submitted) { | ||
| return ( | ||
| <div className="flex flex-col items-center text-center w-90"> | ||
| <div className="flex flex-col items-start gap-6"> | ||
| <h1 className="![font-family:var(--font-heading)] !text-[36px] !font-semibold">Password Changed</h1> | ||
| <h5 className="![font-family:var(--font-body)] !text-[16px] !font-bold text-center !text-core-black !mb-6"> | ||
| Your password has been successfully changed! | ||
| </h5> | ||
| </div> | ||
| <Button | ||
| className="![font-family:var(--font-body)] !rounded !bg-core-green !text-core-white w-full !px-4 !py-1.5 !mb-10" | ||
| onClick={() => router.push('/login')} | ||
| > | ||
| Back to login | ||
| </Button> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-center text-center w-80"> | ||
| <h1 className="![font-family:var(--font-heading)] !text-[36px] !font-semibold !mb-6">Reset Password</h1> | ||
| <div className="flex flex-col gap-4 w-full !mb-10"> | ||
| <TextInputField | ||
| label="New Password *" | ||
| placeholder="Enter new password" | ||
| errorMessage={newPasswordError} | ||
| isError={!!newPasswordError} | ||
| value={newPassword} | ||
| onChange={(value) => setNewPassword(value)} | ||
| /> | ||
| <TextInputField | ||
| label="Confirm Password *" | ||
| placeholder="Retype password" | ||
| errorMessage={confirmPasswordError} | ||
| isError={!!confirmPasswordError} | ||
| value={confirmPassword} | ||
| onChange={(value) => setConfirmPassword(value)} | ||
| /> | ||
| </div> | ||
| <Button | ||
| className="![font-family:var(--font-body)] !rounded !bg-core-green !text-core-white w-full !px-4 !py-1.5 !mb-10" | ||
| onClick={handleResetPassword} | ||
| loading={isLoading} | ||
| > | ||
| Reset Password | ||
| </Button> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default function ResetPasswordPage() { | ||
| return ( | ||
| <Suspense> | ||
| <ResetPasswordContent /> | ||
| </Suspense> | ||
| ); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also curious what this is for