Skip to content

Commit 0a3a7bc

Browse files
authored
Merge pull request #3058 from mozilla/nextjs-authenticated-layout
Add layout for authenticated pages
2 parents 4273a3a + 7f99b0e commit 0a3a7bc

File tree

13 files changed

+476
-82
lines changed

13 files changed

+476
-82
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
const userMenuButton = document.querySelector('.user-menu-button')
6+
const userMenuPopover = document.querySelector('.user-menu-popover')
7+
const userMenuWrapper = document.querySelector('.user-menu-wrapper')
8+
9+
function handleBlur (event, onBlur) {
10+
const currentTarget = event.currentTarget
11+
12+
requestAnimationFrame(() => {
13+
const isChildElement = currentTarget.contains(document.activeElement)
14+
15+
if (!isChildElement) {
16+
onBlur()
17+
}
18+
})
19+
}
20+
21+
function handleMenuButton () {
22+
if (!userMenuPopover || !userMenuWrapper) {
23+
return
24+
}
25+
26+
if (userMenuPopover.hasAttribute('hidden')) {
27+
// Show popover
28+
userMenuPopover.setAttribute('aria-expanded', true)
29+
userMenuPopover.removeAttribute('hidden')
30+
31+
// Handle onblur
32+
userMenuWrapper.addEventListener('blur', (event) => handleBlur(event, handleMenuButton))
33+
userMenuWrapper.focus()
34+
35+
// TODO: Re-enable event gtag events
36+
// window.gtag('event', 'opened_closed_user_menu', { action: 'open' })
37+
} else {
38+
// Hide popover
39+
userMenuPopover.setAttribute('aria-expanded', false)
40+
userMenuPopover.setAttribute('hidden', '')
41+
42+
userMenuButton.focus()
43+
44+
// window.gtag('event', 'opened_closed_user_menu', { action: 'close' })
45+
}
46+
}
47+
48+
if (userMenuButton) {
49+
userMenuButton.addEventListener('click', handleMenuButton)
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
export default async function UserBreaches() {
6+
return <div>Dashboard</div>;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import { ReactNode } from "react";
6+
import { getServerSession } from "next-auth";
7+
import { redirect } from "next/navigation";
8+
import Image from "next/image";
9+
10+
import "../../../../client/css/index.css";
11+
import { UserMenu } from "../../components/client/UserMenu";
12+
import { SiteNavigation } from "../../components/client/SiteNavigation";
13+
import AppConstants from "../../../../appConstants.js";
14+
import MonitorLogo from "../../../../client/images/[email protected]";
15+
import MozillaLogo from "../../../../client/images/moz-logo-1color-white-rgb-01.svg";
16+
import { getL10n } from "../../../functions/server/l10n";
17+
import { authOptions } from "../../../api/auth/[...nextauth]/route";
18+
export type Props = {
19+
children: ReactNode;
20+
};
21+
22+
const MainLayout = async (props: Props) => {
23+
const session = await getServerSession(authOptions);
24+
if (!session) {
25+
redirect("/");
26+
}
27+
28+
const l10n = getL10n();
29+
30+
return (
31+
<>
32+
<header>
33+
<a href="/user/breaches">
34+
<Image
35+
className="monitor-logo"
36+
src={MonitorLogo}
37+
width="213"
38+
height="33"
39+
alt={l10n.getString("brand-fx-monitor")}
40+
/>
41+
</a>
42+
<div className="nav-wrapper">
43+
<button className="nav-toggle">
44+
<svg
45+
xmlns="http://www.w3.org/2000/svg"
46+
viewBox="0 0 10 8"
47+
width="20"
48+
>
49+
<path
50+
d="M1 1h8M1 4h8M1 7h8"
51+
stroke="#000"
52+
strokeWidth="1"
53+
strokeLinecap="round"
54+
/>
55+
</svg>
56+
</button>
57+
<UserMenu
58+
session={session}
59+
fxaSettingsUrl={AppConstants.FXA_SETTINGS_URL}
60+
/>
61+
</div>
62+
</header>
63+
64+
<SiteNavigation />
65+
66+
<main>{props.children}</main>
67+
68+
<footer className="site-footer">
69+
<a href="https://www.mozilla.org" target="_blank">
70+
<Image
71+
src={MozillaLogo}
72+
width="100"
73+
height="29"
74+
loading="lazy"
75+
alt={l10n.getString("mozilla")}
76+
/>
77+
</a>
78+
<menu>
79+
<li>
80+
<a href="/breaches">{l10n.getString("footer-nav-all-breaches")}</a>
81+
</li>
82+
<li>
83+
<a
84+
href="https://support.mozilla.org/kb/firefox-monitor-faq"
85+
target="_blank"
86+
>
87+
FAQ
88+
</a>
89+
</li>
90+
<li>
91+
<a
92+
href="https://www.mozilla.org/privacy/firefox-monitor"
93+
target="_blank"
94+
>
95+
{l10n.getString("terms-and-privacy")}
96+
</a>
97+
</li>
98+
<li>
99+
<a href="https://github.com/mozilla/blurts-server" target="_blank">
100+
{l10n.getString("github")}
101+
</a>
102+
</li>
103+
</menu>
104+
</footer>
105+
</>
106+
);
107+
};
108+
109+
export default MainLayout;

src/app/(nextjs_migration)/(guest)/layout.tsx

+58-25
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,79 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
import { ReactNode } from 'react'
6-
import '../../../client/css/index.css'
7-
import Image from 'next/image'
8-
import MonitorLogo from '../../../client/images/[email protected]'
9-
import MozillaLogo from '../../../client/images/moz-logo-1color-white-rgb-01.svg'
10-
import { getL10n } from '../../functions/server/l10n'
5+
import { ReactNode } from "react";
6+
import "../../../client/css/index.css";
7+
import Image from "next/image";
8+
import MonitorLogo from "../../../client/images/[email protected]";
9+
import MozillaLogo from "../../../client/images/moz-logo-1color-white-rgb-01.svg";
10+
import { SignInButton } from "../components/client/SignInButton";
11+
import { getL10n } from "../../functions/server/l10n";
1112

1213
export type Props = {
13-
children: ReactNode
14-
}
14+
children: ReactNode;
15+
};
1516

1617
const GuestLayout = (props: Props) => {
17-
const l10n = getL10n()
18+
const l10n = getL10n();
1819

1920
return (
2021
<>
2122
<header>
22-
<a href='/'>
23-
<Image className='monitor-logo' src={MonitorLogo} width='213' height='33' alt={l10n.getString('brand-fx-monitor')}/>
23+
<a href="/">
24+
<Image
25+
className="monitor-logo"
26+
src={MonitorLogo}
27+
width="213"
28+
height="33"
29+
alt={l10n.getString("brand-fx-monitor")}
30+
/>
2431
</a>
2532
<menu>
26-
<li><a href='/user/breaches' data-cta-id='sign-in-1' className='button secondary'>{l10n.getString('sign-in')}</a></li>
33+
<li>
34+
<SignInButton />
35+
</li>
2736
</menu>
2837
</header>
29-
<main>
30-
{props.children}
31-
</main>
32-
<footer className='site-footer'>
33-
<a href='https://www.mozilla.org' target='_blank'>
34-
<Image src={MozillaLogo} width='100' height='29' loading='lazy' alt={l10n.getString('mozilla')}/>
38+
<main>{props.children}</main>
39+
<footer className="site-footer">
40+
<a href="https://www.mozilla.org" target="_blank">
41+
<Image
42+
src={MozillaLogo}
43+
width="100"
44+
height="29"
45+
loading="lazy"
46+
alt={l10n.getString("mozilla")}
47+
/>
3548
</a>
3649
<menu>
37-
<li><a href='/breaches'>{l10n.getString('footer-nav-all-breaches')}</a></li>
38-
<li><a href='https://support.mozilla.org/kb/firefox-monitor-faq' target='_blank'>FAQ</a></li>
39-
<li><a href='https://www.mozilla.org/privacy/firefox-monitor' target='_blank'>{l10n.getString('terms-and-privacy')}</a></li>
40-
<li><a href='https://github.com/mozilla/blurts-server' target='_blank'>{l10n.getString('github')}</a></li>
50+
<li>
51+
<a href="/breaches">{l10n.getString("footer-nav-all-breaches")}</a>
52+
</li>
53+
<li>
54+
<a
55+
href="https://support.mozilla.org/kb/firefox-monitor-faq"
56+
target="_blank"
57+
>
58+
FAQ
59+
</a>
60+
</li>
61+
<li>
62+
<a
63+
href="https://www.mozilla.org/privacy/firefox-monitor"
64+
target="_blank"
65+
>
66+
{l10n.getString("terms-and-privacy")}
67+
</a>
68+
</li>
69+
<li>
70+
<a href="https://github.com/mozilla/blurts-server" target="_blank">
71+
{l10n.getString("github")}
72+
</a>
73+
</li>
4174
</menu>
4275
</footer>
4376
</>
44-
)
45-
}
77+
);
78+
};
4679

47-
export default GuestLayout
80+
export default GuestLayout;

src/app/(nextjs_migration)/(guest)/page.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import Image from "next/image";
66
import Script from "next/script";
77
import "../../../client/css/partials/landing.css";
8-
98
import { getL10n } from "../../functions/server/l10n";
109

1110
import HeroImage from "../../../client/images/[email protected]";
@@ -14,7 +13,7 @@ import LockImage from "../../../client/images/[email protected]";
1413
import MailImage from "../../../client/images/[email protected]";
1514
import NaturePhoneImage from "../../../client/images/[email protected]";
1615

17-
export default function Home() {
16+
export default async function Home() {
1817
const l10n = getL10n();
1918

2019
return (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
"use client";
6+
7+
import { signIn } from "next-auth/react";
8+
import { useL10n } from "../../../hooks/l10n";
9+
10+
export const SignInButton = () => {
11+
const l10n = useL10n();
12+
13+
return (
14+
<button
15+
onClick={() => signIn("fxa", { callbackUrl: "/user/breaches" })}
16+
data-cta-id="sign-in-1"
17+
className="button secondary"
18+
>
19+
{l10n.getString("sign-in")}
20+
</button>
21+
);
22+
};

0 commit comments

Comments
 (0)