diff --git a/public/nextjs_migration/client/js/userMenu.js b/public/nextjs_migration/client/js/userMenu.js new file mode 100644 index 00000000000..4720c3b2536 --- /dev/null +++ b/public/nextjs_migration/client/js/userMenu.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const userMenuButton = document.querySelector('.user-menu-button') +const userMenuPopover = document.querySelector('.user-menu-popover') +const userMenuWrapper = document.querySelector('.user-menu-wrapper') + +function handleBlur (event, onBlur) { + const currentTarget = event.currentTarget + + requestAnimationFrame(() => { + const isChildElement = currentTarget.contains(document.activeElement) + + if (!isChildElement) { + onBlur() + } + }) +} + +function handleMenuButton () { + if (!userMenuPopover || !userMenuWrapper) { + return + } + + if (userMenuPopover.hasAttribute('hidden')) { + // Show popover + userMenuPopover.setAttribute('aria-expanded', true) + userMenuPopover.removeAttribute('hidden') + + // Handle onblur + userMenuWrapper.addEventListener('blur', (event) => handleBlur(event, handleMenuButton)) + userMenuWrapper.focus() + + // TODO: Re-enable event gtag events + // window.gtag('event', 'opened_closed_user_menu', { action: 'open' }) + } else { + // Hide popover + userMenuPopover.setAttribute('aria-expanded', false) + userMenuPopover.setAttribute('hidden', '') + + userMenuButton.focus() + + // window.gtag('event', 'opened_closed_user_menu', { action: 'close' }) + } +} + +if (userMenuButton) { + userMenuButton.addEventListener('click', handleMenuButton) +} diff --git a/src/app/(nextjs_migration)/(authenticated)/user/breaches/page.tsx b/src/app/(nextjs_migration)/(authenticated)/user/breaches/page.tsx new file mode 100644 index 00000000000..5c63a275f8a --- /dev/null +++ b/src/app/(nextjs_migration)/(authenticated)/user/breaches/page.tsx @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +export default async function UserBreaches() { + return
Dashboard
; +} diff --git a/src/app/(nextjs_migration)/(authenticated)/user/layout.tsx b/src/app/(nextjs_migration)/(authenticated)/user/layout.tsx new file mode 100644 index 00000000000..ba35d54c9c4 --- /dev/null +++ b/src/app/(nextjs_migration)/(authenticated)/user/layout.tsx @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { ReactNode } from "react"; +import { getServerSession } from "next-auth"; +import { redirect } from "next/navigation"; +import Image from "next/image"; + +import "../../../../client/css/index.css"; +import { UserMenu } from "../../components/client/UserMenu"; +import { SiteNavigation } from "../../components/client/SiteNavigation"; +import AppConstants from "../../../../appConstants.js"; +import MonitorLogo from "../../../../client/images/monitor-logo-transparent@2x.webp"; +import MozillaLogo from "../../../../client/images/moz-logo-1color-white-rgb-01.svg"; +import { getL10n } from "../../../functions/server/l10n"; +import { authOptions } from "../../../api/auth/[...nextauth]/route"; +export type Props = { + children: ReactNode; +}; + +const MainLayout = async (props: Props) => { + const session = await getServerSession(authOptions); + if (!session) { + redirect("/"); + } + + const l10n = getL10n(); + + return ( + <> +
+ + {l10n.getString("brand-fx-monitor")} + +
+ + +
+
+ + + +
{props.children}
+ + + + ); +}; + +export default MainLayout; diff --git a/src/app/(nextjs_migration)/(guest)/layout.tsx b/src/app/(nextjs_migration)/(guest)/layout.tsx index 74731068205..c9649158237 100644 --- a/src/app/(nextjs_migration)/(guest)/layout.tsx +++ b/src/app/(nextjs_migration)/(guest)/layout.tsx @@ -2,46 +2,79 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { ReactNode } from 'react' -import '../../../client/css/index.css' -import Image from 'next/image' -import MonitorLogo from '../../../client/images/monitor-logo-transparent@2x.webp' -import MozillaLogo from '../../../client/images/moz-logo-1color-white-rgb-01.svg' -import { getL10n } from '../../functions/server/l10n' +import { ReactNode } from "react"; +import "../../../client/css/index.css"; +import Image from "next/image"; +import MonitorLogo from "../../../client/images/monitor-logo-transparent@2x.webp"; +import MozillaLogo from "../../../client/images/moz-logo-1color-white-rgb-01.svg"; +import { SignInButton } from "../components/client/SignInButton"; +import { getL10n } from "../../functions/server/l10n"; export type Props = { - children: ReactNode -} + children: ReactNode; +}; const GuestLayout = (props: Props) => { - const l10n = getL10n() + const l10n = getL10n(); return ( <>
- - {l10n.getString('brand-fx-monitor')}/ + + {l10n.getString("brand-fx-monitor")} -
  • {l10n.getString('sign-in')}
  • +
  • + +
  • -
    - {props.children} -
    -