Skip to content

Commit

Permalink
Merge pull request #4146 from mozilla/holiday-promo-code-banner
Browse files Browse the repository at this point in the history
MPP-3588: 2023 Holiday promo banner
  • Loading branch information
rafeerahman authored Nov 22, 2023
2 parents 4ee480f + 37688dd commit 71d56da
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 0 deletions.
10 changes: 10 additions & 0 deletions frontend/pendingTranslations.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,13 @@ profile-free-onboarding--addon-item-headline-1 = They work anywhere you need an
profile-free-onboarding--addon-item-description-1 = Anywhere you need to enter an email, you can use a { -brand-name-relay } email mask. You can even generate new masks directly from your { -brand-name-firefox } password manager.
profile-free-onboarding--addon-item-headline-2 = The { -brand-name-relay } extension makes it easy
profile-free-onboarding--addon-item-description-2 = Whenever you see an email address field, the { -brand-name-firefox-relay } browser extension will autofill any email mask you want to use. No need to remember each mask!
## 2023 Holiday promo

holiday-promo-banner-protect-inbox = Protect your inbox for less
holiday-promo-banner-code-desc = Take 20% off { -brand-name-relay-premium }
# Variables:
# $couponCode (string) - the coupon code.
holiday-promo-banner-code-usage = Use code <coupon>{ $couponCode }</coupon> at checkout
holiday-promo-banner-cta-button = Get 1 year of { -brand-name-premium }
holiday-promo-banner-promo-expiry = offer ends Dec 31, 2023
11 changes: 11 additions & 0 deletions frontend/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { RuntimeData } from "../../hooks/api/runtimeData";
import { useRouter } from "next/router";
import { isPhonesAvailableInCountry } from "../../functions/getPlan";
import { useL10n } from "../../hooks/l10n";
import { HolidayPromoBanner } from "./topmessage/HolidayPromoBanner";
import { isFlagActive } from "../../functions/waffle";

export type Props = {
children: ReactNode;
Expand Down Expand Up @@ -138,6 +140,14 @@ export const Layout = (props: Props) => {
runtimeData={props.runtimeData}
/>
<header className={styles["header-outer"]}>
{isFlagActive(props.runtimeData, "holiday_promo_2023") &&
(router.pathname === "/" || router.pathname === "/premium") ? (
<HolidayPromoBanner
isLoading={profiles.isLoading}
profile={profiles.data?.[0]}
runtimeData={props.runtimeData}
/>
) : null}
{props.theme === "plain" ? (
PlainPageHeader
) : (
Expand All @@ -153,6 +163,7 @@ export const Layout = (props: Props) => {
</>
</Link>
</div>

<div className={styles["nav-wrapper"]}>
<Navigation
mobileMenuExpanded={mobileMenuExpanded}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@import "../../../styles/tokens.scss";
@import "~@mozilla-protocol/core/protocol/css/includes/lib";

// Hardcoding #5015cd after talking with design, no token for this color exists yet in Nebula.
$banner-gradient: linear-gradient(
90.13deg,
$color-purple-90 -2.39%,
#5015cd 50.81%,
$color-purple-50 104%
);

.wrapper {
display: flex;
align-items: center;
justify-content: center;
padding: $spacing-md;
color: $color-white;
position: relative;
background-image: url("../images/holiday-promo-banner-pattern-mobile.svg"),
$banner-gradient;
background-repeat: no-repeat;
background-size: cover;
flex-direction: column;
gap: $spacing-md;

@media screen and #{$mq-sm} {
flex-direction: row;
gap: $spacing-2xl;
background-image: url("../images/holiday-promo-banner-pattern-tablet.svg"),
$banner-gradient;
}
@media screen and #{$mq-md} {
gap: $layout-xl;
background-image: url("../images/holiday-promo-banner-pattern-laptop.svg"),
$banner-gradient;
}
@media screen and #{$mq-xl} {
background-image: url("../images/holiday-promo-banner-pattern-desktop.svg"),
$banner-gradient;
}

.left-promo-container {
display: flex;
align-items: center;
flex-direction: column;
gap: $spacing-md;

@media screen and #{$mq-sm} {
gap: 0;
}
@media screen and #{$mq-lg} {
flex-direction: row;
gap: $layout-xl;
}
}

.promo-container {
text-align: center;
}

.cta-button {
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
background-color: $color-white;
padding: $spacing-sm $spacing-md;
border-radius: $border-radius-sm;
color: $color-purple-90;
font-weight: 700;
margin-bottom: $spacing-xs;

&:hover {
cursor: pointer;
opacity: 0.9;
}
}

.promo-code-expiry {
color: rgba($color-white, 0.7);
font-weight: 700;
display: block;

@include text-body-xs;
}

.banner-pattern {
width: 100%;
position: absolute;
}

.promo-text {
@include text-body-lg;

font-family: $font-stack-base;
}

.promo-text-bolded {
@include text-body-xl;

font-weight: 700;
font-family: $font-stack-firefox;
}
}
67 changes: 67 additions & 0 deletions frontend/src/components/layout/topmessage/HolidayPromoBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Link from "next/link";
import { useL10n } from "../../../hooks/l10n";
import { Localized } from "../../Localized";
import styles from "./HolidayPromoBanner.module.scss";
import { RuntimeData } from "../../../hooks/api/runtimeData";
import {
getPeriodicalPremiumSubscribeLink,
isPeriodicalPremiumAvailableInCountry,
} from "../../../functions/getPlan";
import { ProfileData } from "../../../hooks/api/profile";

type Props = {
isLoading: boolean;
profile?: ProfileData;
runtimeData?: RuntimeData;
};

export const HolidayPromoBanner = (props: Props) => {
const l10n = useL10n();
const coupon = "HOLIDAY20";
const subscribeLink = isPeriodicalPremiumAvailableInCountry(props.runtimeData)
? getPeriodicalPremiumSubscribeLink(props.runtimeData, "yearly")
: null;
const todaysDate = new Date();
const expiryDate = new Date("December 31, 2023");
const isPastExpiry = todaysDate > expiryDate;

if (props.isLoading || !subscribeLink || props.profile || isPastExpiry) {
return null;
}

const subscriberLinkWithCoupon = `${subscribeLink}&coupon=${coupon}`;

return (
<aside className={styles.wrapper}>
<div className={styles["left-promo-container"]}>
<div className={styles["promo-container"]}>
<p className={styles["promo-text"]}>
{l10n.getString("holiday-promo-banner-protect-inbox")}
</p>
<p className={styles["promo-text-bolded"]}>
{l10n.getString("holiday-promo-banner-code-desc")}
</p>
</div>
<div className={styles["promo-container"]}>
<Localized
id="holiday-promo-banner-code-usage"
vars={{ couponCode: coupon }}
elems={{
coupon: <span className={styles["promo-text-bolded"]} />,
}}
>
<p className={styles["promo-text"]} />
</Localized>
</div>
</div>
<div className={styles["promo-container"]}>
<Link href={subscriberLinkWithCoupon} className={styles["cta-button"]}>
{l10n.getString("holiday-promo-banner-cta-button")}
</Link>
<small className={styles["promo-code-expiry"]}>
{l10n.getString("holiday-promo-banner-promo-expiry")}
</small>
</div>
</aside>
);
};
1 change: 1 addition & 0 deletions frontend/src/hooks/api/runtimeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type FlagNames =
| "mailing_list_announcement"
| "premium_promo_banners"
| "mask_redesign"
| "holiday_promo_2023"
| "mobile_app";

type WaffleFlag = [FlagNames, boolean];
Expand Down

0 comments on commit 71d56da

Please sign in to comment.