Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
fd65112
Refactor environment variables to use correct local development URL
EduContin Oct 7, 2024
3fc1e5d
Refactor environment variables and URLs
EduContin Oct 7, 2024
cfe5b25
Testing bare git
EduContin Oct 7, 2024
73a9566
Refactor NEXTAUTH_URL in .env.development to use correct local develo…
EduContin Oct 7, 2024
d050f16
Refactor .env file to use correct local development URL and include P…
EduContin Oct 7, 2024
caade34
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variables
EduContin Oct 7, 2024
cf048d2
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variab…
EduContin Oct 7, 2024
a73554d
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variab…
EduContin Oct 7, 2024
8498eb8
Refactor getSSLValues function to include rejectUnauthorized option
EduContin Oct 7, 2024
a31ce2f
Refactor getSSLValues function to include rejectUnauthorized option
EduContin Oct 8, 2024
4033bd5
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variab…
EduContin Oct 8, 2024
cd9cf70
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variab…
EduContin Oct 8, 2024
147e6a8
Refactor UserProfile component to include user signature in the profi…
EduContin Oct 8, 2024
f602dfd
Refactor .env file to exclude unnecessary entries and add Postgres CA…
EduContin Oct 8, 2024
6988315
Refactor ForumDashboard and Navbar components
EduContin Oct 8, 2024
7e92ca5
Refactor .env file to separate POSTGRES_HOST and POSTGRES_PORT variab…
EduContin Oct 8, 2024
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: 4 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ POSTGRES_PORT=5432
POSTGRES_USER=local_user
POSTGRES_DB=local_db
POSTGRES_PASSWORD=local_password
POSTGRES_CA="-----BEGIN CERTIFICATE-----\nMIIDhjCCAwygAwIBAgISBNzos6EbVpPFn8nem8wBwErWMAoGCCqGSM49BAMDMDIx\nCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF\nNjAeFw0yNDEwMDcxNjAyMThaFw0yNTAxMDUxNjAyMTdaMBUxEzARBgNVBAMTCmFs\ncGVuZWQuaW8wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT3CdKEpCiMAENO9z4R\n3uQPiq/Ww5YKyYEYXdYUsIxNbMm6eeDUHZhC/Dbd+BFwtdUN+0cdZJxGJ7EkN/qu\nPgslo4ICHTCCAhkwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMB\nBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTjwnHYRiaBlBqSK4c1\nsRi6xUvs9jAfBgNVHSMEGDAWgBSTJ0aYA6lRaI6Y1sRCSNsjv1iU0jBVBggrBgEF\nBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9lNi5vLmxlbmNyLm9yZzAiBggr\nBgEFBQcwAoYWaHR0cDovL2U2LmkubGVuY3Iub3JnLzAlBgNVHREEHjAcggphbHBl\nbmVkLmlvgg53d3cuYWxwZW5lZC5pbzATBgNVHSAEDDAKMAgGBmeBDAECATCCAQUG\nCisGAQQB1nkCBAIEgfYEgfMA8QB2AM8RVu7VLnyv84db2Wkum+kacWdKsBfsrAHS\nW3fOzDsIAAABkmftKG4AAAQDAEcwRQIhALewnaUKAvEbs2GE31Yp7k6eBt6pW+2/\nVRgHotXHj68RAiAvnZ6I5YntLrj7Hez7IVgN3dUoVpKr0SyrqIpL29O1NwB3AH1Z\nHhLheCp7HGFnfF79+NCHXBSgTpWeuQMv2Q6MLnm4AAABkmftKDAAAAQDAEgwRgIh\nAJDKFTWpnWYebI+5wxVDeu843KGym7QvX8DXyN6SAIJiAiEA2Ro751daHDfhAKxS\nzT+LWSPWRpSMhCnqHeUqvzUjmG4wCgYIKoZIzj0EAwMDaAAwZQIxAMk8OvpQsgGQ\nM1Cjgp47y+2fI3dBVIyUed9tSYSsreWGSJJdtBlIJr59ZqYscBZ7RAIwFUmWNTJG\n6P+GueQv+BOvR10/DdGo7MvqyqbJoBgFVnpsJ7mRuFgZ3S7X2pdg/10r\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\nWhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\nRW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\nh/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\ngfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\nATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\nv1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\nAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\nBAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\nY3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\nMxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\npMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\neDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\npOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\ns8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\nh4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\nYlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\nZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\nLyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\nEwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\nIg46v9mFmBvyH04=\n-----END CERTIFICATE-----"

DATABASE_URL=postgresql://local_user:local_password@localhost:5432/local_db

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=v5+DnKYpcVpu6/sjEP2AVub1Stoql2qz/RAiwp5udLk

SOCKET_PORT=4000

HOODPAY_API_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIzMjI1IiwiZXhwIjoyMDQzNTQwODY2fQ.ZSzD7Vj2tgOfseg_6PcDhF_PkmGag6slEXUFwz4llpE
HOODPAY_BUSINESS_ID=21578

NEXTAUTH_URL=https://alpened.io
NEXT_PUBLIC_SHOUTBOX_SOCKET=https://alpened.io
NEXT_PUBLIC_APP_URL=https://alpened.io

NEXT_PUBLIC_SHOUTBOX_SOCKET=http://localhost:4000
4 changes: 2 additions & 2 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ SOCKET_PORT=4000

HOODPAY_API_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIzMDM4IiwiZXhwIjoyMDQyOTgxMzU4fQ.ElWanjvJZ_NLr5gvk0XwKGBLMGnU32NG_J-qjtbJhfA
HOODPAY_BUSINESS_ID=21424
NEXT_PUBLIC_APP_URL=http://localhost:3000

NEXT_PUBLIC_SHOUTBOX_SOCKET=http://localhost:4000
NEXT_PUBLIC_SHOUTBOX_SOCKET=http://localhost:4000
NEXT_PUBLIC_APP_URL=http://localhost:3000
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env*.local

# vercel
Expand All @@ -36,4 +37,4 @@ yarn-error.log*
next-env.d.ts

# Socket server
socket-server/node_modules
socket-server/node_modules
67 changes: 49 additions & 18 deletions app/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,49 @@
import React from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { CircularProgress } from "@mui/material";
import ForumSummary from "@/components/ForumSummary";
import StickyTopics from "@/components/AnnouncementsTopics";
import RecentTopics from "@/components/RecentTopics";
import AnimatedDashboard from "@/components/AnimatedDashboard";
import MountainBackground from "@/components/MountainBackground";
import Shoutbox from "@/components/Shoutbox";

export default function ForumDashboard() {
import CircularProgress from "@mui/material/CircularProgress";
import dynamic from "next/dynamic";

const DynamicShoutbox = dynamic(() => import("@/components/Shoutbox"), {
loading: () => <p>Loading Shoutbox...</p>,
});

const DynamicForumSummary = dynamic(() => import("@/components/ForumSummary"), {
loading: () => <p>Loading Forum Summary...</p>,
});

const DynamicStickyTopics = dynamic(
() => import("@/components/AnnouncementsTopics"),
{
loading: () => <p>Loading Sticky Topics...</p>,
},
);

const DynamicRecentTopics = dynamic(() => import("@/components/RecentTopics"), {
loading: () => <p>Loading Recent Topics...</p>,
});

const MemoizedMountainBackground = React.memo(
dynamic(() => import("@/components/MountainBackground")),
);
const DynamicAnimatedDashboard = dynamic(
() => import("@/components/AnimatedDashboard"),
);

interface ForumDashboardProps {
// Add any props if needed
}

const ForumDashboard: React.FC<ForumDashboardProps> = () => {
const { data: session, status } = useSession();
const router = useRouter();

if (status === "loading") {
return <CircularProgress />;
return (
<div className="flex justify-center items-center h-screen">
<CircularProgress />
</div>
);
}

if (status === "unauthenticated") {
Expand All @@ -26,25 +55,27 @@ export default function ForumDashboard() {

return (
<div className="min-h-screen text-white relative">
<MountainBackground isLoading={false} isSuccess={false} />
<MemoizedMountainBackground isLoading={false} isSuccess={false} />
<div className="container mx-auto px-4 py-8 relative z-10">
<AnimatedDashboard>
<DynamicAnimatedDashboard>
<h1 className="text-4xl font-bold mb-8 text-center">
Welcome, {session?.user?.name}
</h1>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
<div className="lg:col-span-2">
<Shoutbox />
<ForumSummary />
<DynamicShoutbox />
<DynamicForumSummary />
</div>
<div>
<StickyTopics />
<RecentTopics />
<DynamicStickyTopics />
<DynamicRecentTopics />
</div>
</div>
<div className="flex justify-between"></div>
</AnimatedDashboard>
</DynamicAnimatedDashboard>
</div>
</div>
);
}
};

export default ForumDashboard;
35 changes: 35 additions & 0 deletions components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// components/ErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from "react";

interface Props {
children: ReactNode;
}

interface State {
hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
};

// eslint-disable-next-line no-unused-vars
public static getDerivedStateFromError(_: Error): State {
return { hasError: true };
}

public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo);
}

public render() {
if (this.state.hasError) {
return <h1>Sorry.. there was an error</h1>;
}

return this.props.children;
}
}

export default ErrorBoundary;
125 changes: 81 additions & 44 deletions components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,96 @@
"use client";

import React, { useState } from "react";
import React, { useState, useCallback, useEffect } from "react";
import { useRouter } from "next/navigation";
import { Alert, Button, Snackbar, IconButton } from "@mui/material";
import { Visibility, VisibilityOff } from "@mui/icons-material";
import { signIn } from "next-auth/react";
import { signIn, useSession } from "next-auth/react";
import Link from "next/link";
import MountainBackground from "./MountainBackground";
import dynamic from "next/dynamic";

const DynamicMountainBackground = dynamic(
() => import("./MountainBackground"),
{
ssr: false,
},
);

export default function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [formData, setFormData] = useState({ username: "", password: "" });
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const router = useRouter();
// eslint-disable-next-line no-unused-vars
const { data: session, status } = useSession();

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError(null);

if (!username.trim() || !password.trim()) {
setError("Username and password are required");
setIsLoading(false);
return;
useEffect(() => {
if (status === "authenticated") {
window.location.href = "/";
}
}, [status, router]);

const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
},
[],
);

try {
const response = (await signIn("credentials", {
username: username.trim(),
password: password.trim(),
redirect: false,
})) as unknown as {
ok: boolean;
json: () => Promise<{ message: string }>;
};
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError(null);

if (response.ok) {
setIsSuccess(true);
router.push("/");
} else {
const data = await response.json();
setError(data.message || "Login failed");
const { username, password } = formData;

if (!username.trim() || !password.trim()) {
setError("Username and password are required");
setIsLoading(false);
return;
}
} catch (err) {
console.error("Login error:", err);
setError("An error occurred. Please try again.");
} finally {
setIsLoading(false);
}
};

try {
const result = await signIn("credentials", {
username: username.trim(),
password: password.trim(),
redirect: false,
});

if (result?.error) {
setError("Login failed");
} else {
setIsSuccess(true);
// The useEffect hook will handle redirection
}
} catch (err) {
console.error("Login error:", err);
setError("An error occurred. Please try again.");
} finally {
setIsLoading(false);
}
},
[formData],
);

const togglePasswordVisibility = useCallback(() => {
setShowPassword((prev) => !prev);
}, []);

if (status === "loading") {
return <div>Loading...</div>;
}

if (status === "authenticated") {
return <div>Redirecting...</div>;
}

return (
<div className="flex min-h-screen flex-col items-center justify-center py-2">
<MountainBackground isLoading={isLoading} isSuccess={isSuccess} />
<DynamicMountainBackground isLoading={isLoading} isSuccess={isSuccess} />
<div className="relative z-10">
<main className="flex w-full flex-1 flex-col items-center justify-center px-20 text-center">
<h1 className="text-4xl font-bold mb-8">Login</h1>
Expand All @@ -67,23 +102,25 @@ export default function LoginForm() {
)}
<input
type="text"
name="username"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
value={formData.username}
onChange={handleInputChange}
className="w-full px-3 py-2 mb-4 border rounded"
autoFocus
/>
<div className="relative mb-4">
<div className="flex items-center">
<input
type={showPassword ? "text" : "password"}
name="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
value={formData.password}
onChange={handleInputChange}
className="w-full px-3 py-2 border rounded"
/>
<IconButton
onClick={() => setShowPassword(!showPassword)}
onClick={togglePasswordVisibility}
className="ml-2"
style={{ color: "inherit" }}
>
Expand All @@ -103,8 +140,8 @@ export default function LoginForm() {
</form>
<p className="mt-4">
Don&apos;t have an account?{" "}
<Link href="/register" legacyBehavior>
<a className="text-blue-500">Register</a>
<Link href="/register" className="text-blue-500">
Register
</Link>
</p>
</main>
Expand Down
Loading