diff --git a/apps/web/public/google.png b/apps/web/public/google.png new file mode 100644 index 00000000..4a9f1042 Binary files /dev/null and b/apps/web/public/google.png differ diff --git a/apps/web/public/landing-placeholder.png b/apps/web/public/landing-placeholder.png new file mode 100644 index 00000000..c611913c Binary files /dev/null and b/apps/web/public/landing-placeholder.png differ diff --git a/apps/web/public/svg/blueCheck.svg b/apps/web/public/svg/blueCheck.svg new file mode 100644 index 00000000..3520aa1e --- /dev/null +++ b/apps/web/public/svg/blueCheck.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/web/src/app/(pages)/(dashboard)/layout.tsx b/apps/web/src/app/(pages)/(dashboard)/layout.tsx index bed75e8c..7416cc00 100644 --- a/apps/web/src/app/(pages)/(dashboard)/layout.tsx +++ b/apps/web/src/app/(pages)/(dashboard)/layout.tsx @@ -1,13 +1,22 @@ +import { auth } from "@cooper/auth"; import { CustomToaster } from "@cooper/ui"; +import { redirect } from "next/navigation"; import HeaderLayout from "~/app/_components/header/header-layout"; import OnboardingWrapper from "~/app/_components/onboarding/onboarding-wrapper"; -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + // Ensure user is authenticated + const session = await auth(); + + if (!session) { + redirect("/"); + } + return ( diff --git a/apps/web/src/app/(pages)/(dashboard)/(roles)/page.tsx b/apps/web/src/app/(pages)/(dashboard)/roles/page.tsx similarity index 98% rename from apps/web/src/app/(pages)/(dashboard)/(roles)/page.tsx rename to apps/web/src/app/(pages)/(dashboard)/roles/page.tsx index a2f98be4..70c35ce7 100644 --- a/apps/web/src/app/(pages)/(dashboard)/(roles)/page.tsx +++ b/apps/web/src/app/(pages)/(dashboard)/roles/page.tsx @@ -278,6 +278,8 @@ export default function Roles() { ); const urlCompany = currentUrl.get("company"); const urlRole = currentUrl.get("role"); + // Don't overwrite URL if it has a role but selectedItem is a company (would clear role) + if (urlRole && !isRole(selectedItem)) return; // Skip URL update only when the URL already matches the selected item if (urlCompany && urlRole && isRole(selectedItem)) { const r = selectedItem as RoleType & { @@ -305,6 +307,9 @@ export default function Roles() { const companySlug = roleItem.companySlug ?? createSlug(companyName); const roleSlug = roleItem.slug; + // Don't push if we don't have a valid role slug (would clear role param) + if (!roleSlug) return; + // Preserve search param const currentSearch = params.get("search"); params.delete("search"); @@ -318,7 +323,7 @@ export default function Roles() { params.set("search", currentSearch); } - router.push(`/?${params.toString()}`); + router.push(`/roles/?${params.toString()}`); } else { // For companies, use the company parameter with the name const companyItem = selectedItem as CompanyType & { slug?: string }; @@ -337,7 +342,7 @@ export default function Roles() { params.set("search", currentSearch); } - router.push(`/?${params.toString()}`); + router.push(`/roles/?${params.toString()}`); } } }, [ @@ -388,9 +393,9 @@ export default function Roles() { // Build new URL with only search param if it exists if (searchParam) { - router.push(`/?search=${searchParam}`); + router.push(`/roles/?search=${searchParam}`); } else { - router.push("/"); + router.push("/roles"); } }; diff --git a/apps/web/src/app/(pages)/(dashboard)/(roles)/role/page.tsx b/apps/web/src/app/(pages)/(dashboard)/roles/role/page.tsx similarity index 100% rename from apps/web/src/app/(pages)/(dashboard)/(roles)/role/page.tsx rename to apps/web/src/app/(pages)/(dashboard)/roles/role/page.tsx diff --git a/apps/web/src/app/(pages)/(landing)/layout.tsx b/apps/web/src/app/(pages)/(landing)/layout.tsx new file mode 100644 index 00000000..bed75e8c --- /dev/null +++ b/apps/web/src/app/(pages)/(landing)/layout.tsx @@ -0,0 +1,19 @@ +import { CustomToaster } from "@cooper/ui"; + +import HeaderLayout from "~/app/_components/header/header-layout"; +import OnboardingWrapper from "~/app/_components/onboarding/onboarding-wrapper"; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + + ); +} diff --git a/apps/web/src/app/(pages)/(landing)/page.tsx b/apps/web/src/app/(pages)/(landing)/page.tsx new file mode 100644 index 00000000..6ad25b8b --- /dev/null +++ b/apps/web/src/app/(pages)/(landing)/page.tsx @@ -0,0 +1,54 @@ +import Image from "next/image"; +import LoginButton from "~/app/_components/auth/login-button"; + +const textOptions = [ + "Insights on interviews, pay, and job experience", + "Side-by-side comparison view of up to three jobs", + "Anonymous reviews to protect identities", +]; +export default function Landing() { + return ( +
+
+
+
+
Real reviews of
+ real co-op experiences +
+
+
+ +
+ Log in with husky.neu.edu email to access reviews +
+
+
+ +
+ {textOptions.map((option) => { + return ( +
+ Blue check +
{option}
+
+ ); + })} +
+
+
+ Landing picture +
+
+ ); +} diff --git a/apps/web/src/app/(pages)/(protected)/review-form/page.tsx b/apps/web/src/app/(pages)/(protected)/review-form/page.tsx index ddd2cd34..19d5b1bc 100644 --- a/apps/web/src/app/(pages)/(protected)/review-form/page.tsx +++ b/apps/web/src/app/(pages)/(protected)/review-form/page.tsx @@ -233,7 +233,7 @@ export default function ReviewForm() { const mutation = api.review.create.useMutation({ onSuccess: () => { - router.push("/"); + router.push("/roles"); }, onError: (error) => { toast.error(error.message || "Something went wrong. Please try again."); diff --git a/apps/web/src/app/_components/auth/login-button.tsx b/apps/web/src/app/_components/auth/login-button.tsx index 78092385..e5a0192d 100644 --- a/apps/web/src/app/_components/auth/login-button.tsx +++ b/apps/web/src/app/_components/auth/login-button.tsx @@ -1,17 +1,27 @@ import { signIn } from "@cooper/auth"; import { Button } from "@cooper/ui/button"; +import Image from "next/image"; export default function LoginButton() { return (
); diff --git a/apps/web/src/app/_components/companies/all-company-roles.tsx b/apps/web/src/app/_components/companies/all-company-roles.tsx index 07105eed..d4b951b7 100644 --- a/apps/web/src/app/_components/companies/all-company-roles.tsx +++ b/apps/web/src/app/_components/companies/all-company-roles.tsx @@ -39,10 +39,12 @@ export default function RenderAllRoles({
{ + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); onClose?.(); router.push( - `/?company=${company?.slug ?? ""}&role=${role.slug}&type=roles`, + `/roles/?company=${company?.slug ?? ""}&type=roles&role=${role.slug}`, ); }} > diff --git a/apps/web/src/app/_components/header/header-layout.tsx b/apps/web/src/app/_components/header/header-layout.tsx index f089dd9c..458f69d7 100644 --- a/apps/web/src/app/_components/header/header-layout.tsx +++ b/apps/web/src/app/_components/header/header-layout.tsx @@ -3,7 +3,6 @@ import type { ReactNode } from "react"; import { auth } from "@cooper/auth"; import Header from "~/app/_components/header/header"; -import LoginButton from "../auth/login-button"; import ProfileButton from "../profile/profile-button"; /** @@ -17,16 +16,12 @@ export default async function HeaderLayout({ children: ReactNode; }) { const session = await auth(); - const button = session ? ( - - ) : ( - - ); + const button = session ? : ""; return (
-
+
{children} diff --git a/apps/web/src/app/_components/header/header.tsx b/apps/web/src/app/_components/header/header.tsx index d343f4c1..a08565ca 100644 --- a/apps/web/src/app/_components/header/header.tsx +++ b/apps/web/src/app/_components/header/header.tsx @@ -17,16 +17,18 @@ import { api } from "~/trpc/react"; import { handleSignOut } from "../auth/actions"; import CooperLogo from "../cooper-logo"; import MobileHeaderButton from "./mobile-header-button"; +import { Session } from "@cooper/auth"; interface HeaderProps { auth: React.ReactNode; + loggedIn: Session | null; } /** * This is the header component. (Probably) should use header-layout instead * @returns The header component for the website */ -export default function Header({ auth }: HeaderProps) { +export default function Header({ auth, loggedIn }: HeaderProps) { const [isOpen, setIsOpen] = useState(false); const session = api.auth.getSession.useQuery(); const utils = api.useUtils(); @@ -36,10 +38,10 @@ export default function Header({ auth }: HeaderProps) {
{ e.preventDefault(); - window.location.href = "/"; + window.location.href = "/roles"; }} >

Cooper

@@ -57,7 +59,7 @@ export default function Header({ auth }: HeaderProps) {
setIsOpen(false)} @@ -91,9 +93,9 @@ export default function Header({ auth }: HeaderProps) {
- {showFavorite && } + {showFavorite && ( +
e.stopPropagation()}> + +
+ )}
); diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index d8fac7c7..11133ba9 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -27,6 +27,8 @@ export const metadata: Metadata = { }; export default function RootLayout(props: { children: React.ReactNode }) { + // Ensure user is authenticated + return (