Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c5511e8
Create Button component
jzgom067 Oct 30, 2025
786f61e
Refine mobile shrinking logic
jzgom067 Oct 30, 2025
d66ee49
Remove inaccurate comment
jzgom067 Oct 30, 2025
975726c
Add dynamic padding scaling
jzgom067 Oct 30, 2025
8ade333
Add button style classes
jzgom067 Oct 30, 2025
8d1e817
Add default value to shrinkOnMobile
jzgom067 Oct 30, 2025
d77449c
Move style classes assembly to a helper function
jzgom067 Oct 30, 2025
3af4c27
Create LoadingSpinner component
jzgom067 Oct 30, 2025
935abdb
Create centered absolute class
jzgom067 Oct 30, 2025
a8a48b0
Add loading state
jzgom067 Oct 30, 2025
39f1a7f
Add releaseOnSuccess prop
jzgom067 Oct 30, 2025
860774f
Implement disabled button styling
jzgom067 Oct 30, 2025
1646af7
Adjust loading spinner class application order
jzgom067 Oct 31, 2025
1bf94d2
Add button styles for disabled and hover/active
jzgom067 Oct 31, 2025
7ecd849
Move button to a feature folder
jzgom067 Oct 31, 2025
cd86d35
Add stricter type checking to button props
jzgom067 Oct 31, 2025
3b55681
Remove tooltip comment
jzgom067 Oct 31, 2025
41d9c8b
Add proper documentation to ButtonProps
jzgom067 Oct 31, 2025
dfe7678
Add custom focus ring to buttons
jzgom067 Oct 31, 2025
bb82319
Add loading prop to button component
jzgom067 Oct 31, 2025
db30361
Reorganize button props
jzgom067 Oct 31, 2025
9dc107e
Change button to BaseButton
jzgom067 Oct 31, 2025
129bf1f
Create ActionButton and LinkButton
jzgom067 Oct 31, 2025
e0effd8
Change href and onClick to required
jzgom067 Oct 31, 2025
3a67667
Add forwardRef to buttons and change style prop
jzgom067 Oct 31, 2025
036407e
Convert header buttons to new component
jzgom067 Oct 31, 2025
f73204a
Remove console.log
jzgom067 Oct 31, 2025
e0a7347
Update base auth page buttons
jzgom067 Nov 1, 2025
1912963
Update message page button types
jzgom067 Nov 1, 2025
6a645ed
Change CopyToast to use new button
jzgom067 Nov 1, 2025
465805b
Update buttons on painting and results pages
jzgom067 Nov 1, 2025
8e05f6a
Update button on error page
jzgom067 Nov 1, 2025
ace19da
Update landing page buttons
jzgom067 Nov 1, 2025
43c0a87
Slightly adjust left padding with icon
jzgom067 Nov 1, 2025
22047f6
Update event editor button
jzgom067 Nov 1, 2025
fa610e3
Merge branch 'main-preview' into button-component
jzgom067 Nov 2, 2025
3f091bd
Create ButtonArray type
jzgom067 Nov 2, 2025
170362c
Create mobile footer tray
jzgom067 Nov 2, 2025
9f92bdb
Consolidate action buttons on editor and painting
jzgom067 Nov 2, 2025
fa8fa0f
Update BaseButton to use centralized colors
jzgom067 Nov 2, 2025
367b023
Update colors on dashboard event buttons
jzgom067 Nov 2, 2025
33b7a93
Enforce no text wrapping on buttons
jzgom067 Nov 2, 2025
9ad879d
Remove redundant prop documentation
jzgom067 Nov 2, 2025
bc0a6be
Rename button files
jzgom067 Nov 3, 2025
4f1b3b5
Change group-focus to group-focus-visible
jzgom067 Nov 3, 2025
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
75 changes: 37 additions & 38 deletions src/app/(auth)/forgot-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,49 @@
"use client";

import React, { useRef, useState } from "react";
import { useState } from "react";

import Link from "next/link";
import { useRouter } from "next/navigation";

import MessagePage from "@/components/layout/message-page";
import LinkText from "@/components/link-text";
import TextInputField from "@/features/auth/components/text-input-field";
import ActionButton from "@/features/button/components/action";
import LinkButton from "@/features/button/components/link";
import formatApiError from "@/lib/utils/api/format-api-error";

export default function Page() {
const [email, setEmail] = useState("");
const [emailSent, setEmailSent] = useState(false);
const isSubmitting = useRef(false);
const router = useRouter();

const handleSubmit = async (e: React.FormEvent) => {
const stopRefresh = (e: React.FormEvent) => {
e.preventDefault();
};

if (isSubmitting.current) return;
isSubmitting.current = true;

const handleSubmit = async () => {
if (!email) {
alert("Missing email");
isSubmitting.current = false;
return;
return false;
}

await fetch("/api/auth/start-password-reset/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
})
.then(async (res) => {
if (res.ok) {
setEmailSent(true);
} else {
alert(formatApiError(await res.json()));
}
})
.catch((err) => {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
try {
const res = await fetch("/api/auth/start-password-reset/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});

isSubmitting.current = false;
if (res.ok) {
setEmailSent(true);
return true;
} else {
alert(formatApiError(await res.json()));
return false;
}
} catch (err) {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
return false;
}
};

return (
Expand All @@ -55,16 +53,17 @@ export default function Page() {
title="Check your email"
description={`A password reset link was sent to ${email}.`}
buttons={[
{
type: "primary",
label: "back to login",
onClick: () => router.push("/login"),
},
<LinkButton
key="0"
buttonStyle="primary"
label="Back to Login"
href="/login"
/>,
]}
/>
) : (
<form
onSubmit={handleSubmit}
onSubmit={stopRefresh}
className="flex w-80 flex-col items-center"
>
{/* Title */}
Expand All @@ -87,12 +86,12 @@ export default function Page() {
</Link>

{/* Email Button */}
<button
type="submit"
className="bg-blue dark:bg-red mb-2 cursor-pointer gap-2 rounded-full px-4 py-2 font-medium text-white transition"
>
Send Link
</button>
<ActionButton
buttonStyle="primary"
label="Send Link"
onClick={handleSubmit}
loadOnSuccess
/>
</div>
</form>
)}
Expand Down
70 changes: 34 additions & 36 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"use client";

import React, { useRef, useState, useContext } from "react";
import React, { useContext, useState } from "react";

import Link from "next/link";
import { useRouter } from "next/navigation";

import Checkbox from "@/components/checkbox";
import LinkText from "@/components/link-text";
import TextInputField from "@/features/auth/components/text-input-field";
import ActionButton from "@/features/button/components/action";
import { LoginContext } from "@/lib/providers";
import formatApiError from "@/lib/utils/api/format-api-error";

Expand All @@ -16,50 +17,47 @@ export default function Page() {
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const { setLoggedIn } = useContext(LoginContext);
const isSubmitting = useRef(false);
const router = useRouter();

const handleSubmit = async (e: React.FormEvent) => {
const stopRefresh = (e: React.FormEvent) => {
e.preventDefault();
};

if (isSubmitting.current) return;
isSubmitting.current = true;

const handleSubmit = async () => {
if (!email) {
alert("Missing email");
isSubmitting.current = false;
return;
return false;
}
if (!password) {
alert("Missing password");
isSubmitting.current = false;
return;
return false;
}

await fetch("/api/auth/login/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password, remember_me: rememberMe }),
})
.then(async (res) => {
if (res.ok) {
setLoggedIn(true);
router.push("/dashboard");
} else {
alert(formatApiError(await res.json()));
}
})
.catch((err) => {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
try {
const res = await fetch("/api/auth/login/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password, remember_me: rememberMe }),
});

isSubmitting.current = false;
if (res.ok) {
setLoggedIn(true);
router.push("/dashboard");
return true;
} else {
alert(formatApiError(await res.json()));
return false;
}
} catch (err) {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
return false;
}
};

return (
<div className="flex h-screen items-center justify-center">
<form onSubmit={handleSubmit} className="flex w-80 flex-col items-center">
<form onSubmit={stopRefresh} className="flex w-80 flex-col items-center">
{/* Title */}
<h1 className="font-display text-lion mb-4 block text-5xl leading-none md:text-8xl">
login
Expand All @@ -81,7 +79,7 @@ export default function Page() {
onChange={setPassword}
/>

<div className="flex w-full justify-between">
<div className="flex w-full items-start justify-between">
<div className="m-0 flex flex-col gap-2">
{/* Remember Me Checkbox */}
<Checkbox
Expand All @@ -96,16 +94,16 @@ export default function Page() {
</div>

{/* Login Button */}
<button
type="submit"
className="bg-blue dark:bg-red mb-2 cursor-pointer gap-2 rounded-full px-4 py-2 font-medium text-white transition"
>
Login
</button>
<ActionButton
buttonStyle="primary"
label="Login"
onClick={handleSubmit}
loadOnSuccess
/>
</div>

{/* Register Link */}
<div className="w-full text-right text-xs">
<div className="mt-2 w-full text-right text-xs">
No account?{" "}
<Link href="/register">
<LinkText>Register!</LinkText>
Expand Down
60 changes: 33 additions & 27 deletions src/app/(auth)/register/email-sent/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";

import MessagePage from "@/components/layout/message-page";
import ActionButton from "@/features/button/components/action";
import LinkButton from "@/features/button/components/link";
import formatApiError from "@/lib/utils/api/format-api-error";

export default function Page() {
Expand Down Expand Up @@ -36,27 +38,29 @@ export default function Page() {
timeLeft = Math.ceil(timeLeft);
if (timeLeft > 0) {
alert(`Slow down! ${timeLeft} seconds until you can send again.`);
return;
return false;
}

await fetch("/api/auth/resend-register-email/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
})
.then(async (res) => {
if (res.ok) {
alert("Email resent. Please check your inbox.");
} else {
alert(formatApiError(await res.json()));
}
})
.catch((err) => {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
try {
const res = await fetch("/api/auth/resend-register-email/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});

lastEmailResend.current = Date.now();
if (res.ok) {
alert("Email resent. Please check your inbox.");
lastEmailResend.current = Date.now();
return true;
} else {
alert(formatApiError(await res.json()));
return false;
}
} catch (err) {
console.error("Fetch error:", err);
alert("An error occurred. Please try again.");
return false;
}
};

return (
Expand All @@ -65,16 +69,18 @@ export default function Page() {
title="Check your email"
description={`A verification link was sent to ${email}.`}
buttons={[
{
type: "secondary",
label: "Resend Email",
onClick: handleResendEmail,
},
{
type: "primary",
label: "Go to Login",
onClick: () => router.push("/login"),
},
<ActionButton
key="0"
buttonStyle="secondary"
label="Resend Email"
onClick={handleResendEmail}
/>,
<LinkButton
key="1"
buttonStyle="primary"
label="Go to Login"
href="/login"
/>,
]}
/>
</div>
Expand Down
Loading