From 4c7ed2e2587f8da843c00b95989c44d52784f92c Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 27 Aug 2026 01:21:44 +0800 Subject: [PATCH 1/4] feat: create pm2 deployment config --- ecosystem.config.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 ecosystem.config.js diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..21de3d7 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,16 @@ +module.exports = { + apps: [ + { + name: "devpulse", + script: "./node_modules/next/dist/bin/next", + args: "start", + instances: "max", + exec_mode: "cluster", + max_memory_restart: "1G", + env: { + NODE_ENV: "production", + PORT: 3000, + }, + }, + ], +}; From 7f68f159924978371207e1c5a27a62ad9b79ac67 Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 27 Aug 2026 01:24:19 +0800 Subject: [PATCH 2/4] fix: logout redirects /d/logout --- app/components/common/NavProfileDropdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/common/NavProfileDropdown.tsx b/app/components/common/NavProfileDropdown.tsx index 32bb460..94b27bb 100644 --- a/app/components/common/NavProfileDropdown.tsx +++ b/app/components/common/NavProfileDropdown.tsx @@ -72,7 +72,7 @@ export default function NavProfileDropdown({ setProfileOpen(false)} > @@ -144,7 +144,7 @@ export default function NavProfileDropdown({ {type === "navbar" && ( setProfileOpen(false)} > From 3e798a7ed95f182c58736126d190007812aaf5cb Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 27 Aug 2026 01:32:55 +0800 Subject: [PATCH 3/4] fix: move setLoading and router.push out of toast.promise render callbacks to avoid setState-during-render --- app/components/auth/VerifyEmail.tsx | 2 +- app/components/auth/VerifyWakatime.tsx | 17 +++++++------- app/components/auth/form/LoginForm.tsx | 11 ++++++---- .../auth/form/ResetPasswordForm.tsx | 22 +++++++++---------- app/components/auth/form/SignupForm.tsx | 18 +++++++-------- .../auth/form/UpdatePasswordForm.tsx | 12 +++++----- app/components/dashboard/Settings/Profile.tsx | 16 ++++++++------ .../dashboard/Settings/ResetPassword.tsx | 12 +++++----- 8 files changed, 55 insertions(+), 55 deletions(-) diff --git a/app/components/auth/VerifyEmail.tsx b/app/components/auth/VerifyEmail.tsx index 7c79f34..460af72 100644 --- a/app/components/auth/VerifyEmail.tsx +++ b/app/components/auth/VerifyEmail.tsx @@ -75,7 +75,7 @@ export default function VerifyEmail({ error: "Failed to resend. Please try again.", }); - verifyEmailPromise.then(() => { + verifyEmailPromise.finally(() => { setLoading(false); }); }; diff --git a/app/components/auth/VerifyWakatime.tsx b/app/components/auth/VerifyWakatime.tsx index 674af20..9e0faaa 100644 --- a/app/components/auth/VerifyWakatime.tsx +++ b/app/components/auth/VerifyWakatime.tsx @@ -57,21 +57,22 @@ export default function VerifyWakatime() { toast.promise(verifyWakatimePromise, { pending: "Verifying...", - success: { - render() { - router.push("/d"); - - return "Verification successful!"; - }, - }, + success: "Verification successful!", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to verify. Please try again."; }, }, }); + + verifyWakatimePromise + .then(() => { + router.push("/d"); + }) + .finally(() => { + setLoading(false); + }); }; return ( diff --git a/app/components/auth/form/LoginForm.tsx b/app/components/auth/form/LoginForm.tsx index 496ce44..1a0780b 100644 --- a/app/components/auth/form/LoginForm.tsx +++ b/app/components/auth/form/LoginForm.tsx @@ -81,16 +81,19 @@ export default function LoginForm() { success: "Login successful! Redirecting...", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to login. Please try again."; }, }, }); - loginPromise.then(() => { - router.push(redirectTo); - }); + loginPromise + .then(() => { + router.push(redirectTo); + }) + .finally(() => { + setLoading(false); + }); }; return ( diff --git a/app/components/auth/form/ResetPasswordForm.tsx b/app/components/auth/form/ResetPasswordForm.tsx index 8bac0bb..497fd97 100644 --- a/app/components/auth/form/ResetPasswordForm.tsx +++ b/app/components/auth/form/ResetPasswordForm.tsx @@ -74,26 +74,24 @@ export default function ResetPasswordForm() { toast.promise(updatePasswordPromise, { pending: "Resetting password...", - success: { - render() { - setLoading(false); - setPassword(""); - setConfirmPassword(""); - return "Password updated! You can log in now."; - }, - }, + success: "Password updated! You can log in now.", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to reset password. Please try again."; }, }, }); - updatePasswordPromise.then(() => { - router.replace("/login"); - }); + updatePasswordPromise + .then(() => { + setPassword(""); + setConfirmPassword(""); + router.replace("/login"); + }) + .finally(() => { + setLoading(false); + }); }; return ( diff --git a/app/components/auth/form/SignupForm.tsx b/app/components/auth/form/SignupForm.tsx index ee55275..4309605 100644 --- a/app/components/auth/form/SignupForm.tsx +++ b/app/components/auth/form/SignupForm.tsx @@ -86,22 +86,22 @@ export default function SignupForm() { toast.promise(signUp, { pending: "Creating account...", - success: { - render() { - setLoading(false); - router.push(`/verify-email?email=${encodeURIComponent(email)}`); - - return "Please check your email to verify your account."; - }, - }, + success: "Please check your email to verify your account.", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to sign up. Please try again."; }, }, }); + + signUp + .then(() => { + router.push(`/verify-email?email=${encodeURIComponent(email)}`); + }) + .finally(() => { + setLoading(false); + }); }; return ( diff --git a/app/components/auth/form/UpdatePasswordForm.tsx b/app/components/auth/form/UpdatePasswordForm.tsx index 9e8cb93..c3ccc87 100644 --- a/app/components/auth/form/UpdatePasswordForm.tsx +++ b/app/components/auth/form/UpdatePasswordForm.tsx @@ -69,20 +69,18 @@ export default function UpdatePasswordForm() { toast.promise(updateUserPasswordPromise, { pending: "Updating password...", - success: { - render() { - setLoading(false); - return "Password updated!"; - }, - }, + success: "Password updated!", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to update password. Please try again."; }, }, }); + + updateUserPasswordPromise.finally(() => { + setLoading(false); + }); }; return ( diff --git a/app/components/dashboard/Settings/Profile.tsx b/app/components/dashboard/Settings/Profile.tsx index b34088b..79fab27 100644 --- a/app/components/dashboard/Settings/Profile.tsx +++ b/app/components/dashboard/Settings/Profile.tsx @@ -65,19 +65,21 @@ export default function UserProfile({ user }: { user: UserShape }) { success: "Profile updated!", error: { render({ data }) { - setLoading(false); const err = data as Error; return err?.message || "Failed to update profile. Please try again."; }, }, }); - updateUserProfile.then(() => { - setLoading(false); - setOriginalName(sanitizedName); - setName(sanitizedName); - setIsEditing(false); - }); + updateUserProfile + .then(() => { + setOriginalName(sanitizedName); + setName(sanitizedName); + setIsEditing(false); + }) + .finally(() => { + setLoading(false); + }); } return ( diff --git a/app/components/dashboard/Settings/ResetPassword.tsx b/app/components/dashboard/Settings/ResetPassword.tsx index 6c40e0c..274a3fd 100644 --- a/app/components/dashboard/Settings/ResetPassword.tsx +++ b/app/components/dashboard/Settings/ResetPassword.tsx @@ -67,15 +67,9 @@ export default function ResetPassword({ email }: { email: string }) { toast.promise(resetUserPasswordPromise, { pending: "Sending reset email...", - success: { - render() { - setLoading(false); - return "Reset email sent!"; - }, - }, + success: "Reset email sent!", error: { render({ data }) { - setLoading(false); const err = data as Error; return ( err?.message || "Failed to send reset email. Please try again." @@ -83,6 +77,10 @@ export default function ResetPassword({ email }: { email: string }) { }, }, }); + + resetUserPasswordPromise.finally(() => { + setLoading(false); + }); }; return ( From 6789d7f270dd03abae65c8cf2c48cdde5dc56cb9 Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 27 Aug 2026 01:56:14 +0800 Subject: [PATCH 4/4] feat: update devpulse icon & branding --- app/(public)/(auth)/reset-password/page.tsx | 4 +- app/(public)/flex/page.tsx | 2 +- app/(public)/join/page.tsx | 2 +- app/(public)/leaderboard/[slug]/page.tsx | 2 +- app/(public)/leaderboard/page.tsx | 2 +- app/components/auth/ForgotPassword.tsx | 4 +- app/components/auth/Login.tsx | 4 +- app/components/auth/Signup.tsx | 4 +- app/components/auth/VerifyEmail.tsx | 4 +- app/components/auth/VerifyWakatime.tsx | 4 +- app/components/dashboard/Navbar.tsx | 2 +- app/components/dashboard/Settings/Profile.tsx | 2 +- app/components/layout/Nav.tsx | 2 +- app/components/leaderboard/Header.tsx | 2 +- app/d/update-password/page.tsx | 2 +- app/layout.tsx | 40 +++++++++++- app/page.tsx | 61 +++++++++--------- public/android-chrome-192x192.png | Bin 0 -> 34818 bytes public/android-chrome-512x512.png | Bin 0 -> 110789 bytes public/apple-touch-icon.png | Bin 0 -> 30962 bytes public/favicon-16x16.png | Bin 0 -> 759 bytes public/favicon-32x32.png | Bin 0 -> 2079 bytes public/favicon.ico | Bin 4286 -> 15406 bytes public/favicon.png | Bin 29207 -> 0 bytes public/images/devpulse.cover.png | Bin 213751 -> 115197 bytes public/logo.svg | 33 ---------- public/robots.txt | 2 +- public/site.webmanifest | 1 + 28 files changed, 90 insertions(+), 89 deletions(-) create mode 100644 public/android-chrome-192x192.png create mode 100644 public/android-chrome-512x512.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png delete mode 100644 public/favicon.png delete mode 100644 public/logo.svg create mode 100644 public/site.webmanifest diff --git a/app/(public)/(auth)/reset-password/page.tsx b/app/(public)/(auth)/reset-password/page.tsx index cf5da8e..aa71c8c 100644 --- a/app/(public)/(auth)/reset-password/page.tsx +++ b/app/(public)/(auth)/reset-password/page.tsx @@ -58,7 +58,7 @@ export default async function ResetPassword() { href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -126,7 +126,7 @@ export default async function ResetPassword() { href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/(public)/flex/page.tsx b/app/(public)/flex/page.tsx index 9f460ea..77c217a 100644 --- a/app/(public)/flex/page.tsx +++ b/app/(public)/flex/page.tsx @@ -69,7 +69,7 @@ export default async function Flexs() {
- Devpulse Logo + Devpulse Logo

Devpulse Flexes

diff --git a/app/(public)/join/page.tsx b/app/(public)/join/page.tsx index d3fd77e..d828a9e 100644 --- a/app/(public)/join/page.tsx +++ b/app/(public)/join/page.tsx @@ -160,7 +160,7 @@ export default async function JoinPage({ searchParams }: Props) {
- Devpulse + Devpulse
diff --git a/app/(public)/leaderboard/[slug]/page.tsx b/app/(public)/leaderboard/[slug]/page.tsx index b9f0d38..4667dcc 100644 --- a/app/(public)/leaderboard/[slug]/page.tsx +++ b/app/(public)/leaderboard/[slug]/page.tsx @@ -89,7 +89,7 @@ export default async function LeaderboardPage(props: {
Devpulse Logo
- Devpulse Logo + Devpulse Logo

Devpulse Leaderboards

diff --git a/app/components/auth/ForgotPassword.tsx b/app/components/auth/ForgotPassword.tsx index 24242ac..af9b4a9 100644 --- a/app/components/auth/ForgotPassword.tsx +++ b/app/components/auth/ForgotPassword.tsx @@ -29,7 +29,7 @@ export default function ForgotPassword() { href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -95,7 +95,7 @@ export default function ForgotPassword() { href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/components/auth/Login.tsx b/app/components/auth/Login.tsx index ee265b7..fdcf7e2 100644 --- a/app/components/auth/Login.tsx +++ b/app/components/auth/Login.tsx @@ -29,7 +29,7 @@ export default function Login() { href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -94,7 +94,7 @@ export default function Login() { href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/components/auth/Signup.tsx b/app/components/auth/Signup.tsx index a2d1351..13bd702 100644 --- a/app/components/auth/Signup.tsx +++ b/app/components/auth/Signup.tsx @@ -29,7 +29,7 @@ export default function Signup() { href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -94,7 +94,7 @@ export default function Signup() { href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/components/auth/VerifyEmail.tsx b/app/components/auth/VerifyEmail.tsx index 460af72..669ee40 100644 --- a/app/components/auth/VerifyEmail.tsx +++ b/app/components/auth/VerifyEmail.tsx @@ -91,7 +91,7 @@ export default function VerifyEmail({ href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -157,7 +157,7 @@ export default function VerifyEmail({ href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/components/auth/VerifyWakatime.tsx b/app/components/auth/VerifyWakatime.tsx index 9e0faaa..e3818c8 100644 --- a/app/components/auth/VerifyWakatime.tsx +++ b/app/components/auth/VerifyWakatime.tsx @@ -86,7 +86,7 @@ export default function VerifyWakatime() { href="/" className="flex items-center gap-3 w-fit hover:opacity-80 transition" > - Devpulse Logo + Devpulse Logo Devpulse @@ -152,7 +152,7 @@ export default function VerifyWakatime() { href="/" className="lg:hidden flex items-center justify-center gap-3 mb-10" > - Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/components/dashboard/Navbar.tsx b/app/components/dashboard/Navbar.tsx index 78c9232..b950cc2 100644 --- a/app/components/dashboard/Navbar.tsx +++ b/app/components/dashboard/Navbar.tsx @@ -184,7 +184,7 @@ export default function DashboardLayout({ >
- Devpulse + Devpulse Devpulse
diff --git a/app/components/dashboard/Settings/Profile.tsx b/app/components/dashboard/Settings/Profile.tsx index 79fab27..64f0d49 100644 --- a/app/components/dashboard/Settings/Profile.tsx +++ b/app/components/dashboard/Settings/Profile.tsx @@ -22,7 +22,7 @@ export default function UserProfile({ user }: { user: UserShape }) { const [loading, setLoading] = useState(false); const [isEditing, setIsEditing] = useState(false); const { badWords } = useBadWords(); - const preferredAvatar = user.image || "/logo.svg"; + const preferredAvatar = user.image || "/apple-touch-icon.png"; const isEdited = name.trim() !== originalName.trim(); diff --git a/app/components/layout/Nav.tsx b/app/components/layout/Nav.tsx index 0a2f4dd..d16a3a5 100644 --- a/app/components/layout/Nav.tsx +++ b/app/components/layout/Nav.tsx @@ -14,7 +14,7 @@ export default async function Nav() { className="flex items-center gap-3 hover:opacity-80 transition" data-aos="fade-down" > - Devpulse Logo + Devpulse Logo Devpulse diff --git a/app/components/leaderboard/Header.tsx b/app/components/leaderboard/Header.tsx index 7778873..4fef554 100644 --- a/app/components/leaderboard/Header.tsx +++ b/app/components/leaderboard/Header.tsx @@ -36,7 +36,7 @@ export default function LeaderboardHeader({
Devpulse Logo
- Devpulse Logo + Devpulse Logo

Devpulse

diff --git a/app/layout.tsx b/app/layout.tsx index 591c14e..96c67f9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -83,7 +83,7 @@ export async function generateMetadata(): Promise { }, { rel: "icon", - url: "/favicon.png", + url: "/favicon-32x32.png", }, ], }; @@ -104,16 +104,52 @@ export default function RootLayout({ data-scroll-behavior="smooth" > + + + + + + + + + + + {children} + + {isProduction && ( <> diff --git a/app/page.tsx b/app/page.tsx index fde4032..43c256f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -110,7 +110,7 @@ export default async function Home() { "@type": "Organization", name: "Devpulse", url: "https://devpulse.hallofcodes.org", - logo: "https://devpulse.hallofcodes.org/favicon.png", + logo: "https://devpulse.hallofcodes.org/favicon-32x32.png", }, }; @@ -138,26 +138,12 @@ export default async function Home() {