Skip to content

Commit

Permalink
feat(i18n): format currency using Sanity site locale and currency fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiazom committed Sep 16, 2024
1 parent 5b415d7 commit 4d3c9ca
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 16 deletions.
20 changes: 17 additions & 3 deletions src/app/(main)/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ import { loadQuery } from "studio/lib/store";
import CompensationsPreview from "src/compensations/CompensationsPreview";
import { homeLink } from "../../../blog/components/utils/linkTypes";
import CustomErrorMessage from "../../../blog/components/customErrorMessage/CustomErrorMessage";
import { CompanyLocation } from "studio/lib/payloads/companyDetails";
import { COMPANY_LOCATIONS_QUERY } from "studio/lib/queries/companyDetails";
import {
CompanyInfo,
CompanyLocation,
} from "studio/lib/payloads/companyDetails";
import {
COMPANY_INFO_QUERY,
COMPANY_LOCATIONS_QUERY,
} from "studio/lib/queries/companyDetails";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -52,6 +58,7 @@ async function Page({ params }: Props) {
initialBlogPage,
initialCompensationsPage,
initialLocationsData,
initialCompanyInfo,
] = await Promise.all([
loadQuery<PageBuilder>(SLUG_QUERY, { slug }, { perspective }),
loadQuery<BlogPage>(BLOG_PAGE_QUERY, { slug }, { perspective }),
Expand All @@ -61,6 +68,7 @@ async function Page({ params }: Props) {
{ perspective },
),
loadQuery<CompanyLocation[]>(COMPANY_LOCATIONS_QUERY, {}, { perspective }),
loadQuery<CompanyInfo>(COMPANY_INFO_QUERY, {}, { perspective }),
]);

if (initialPage.data) {
Expand Down Expand Up @@ -106,16 +114,22 @@ async function Page({ params }: Props) {
);
}

if (initialCompensationsPage.data && initialLocationsData.data) {
if (
initialCompensationsPage.data &&
initialLocationsData.data &&
initialCompanyInfo.data
) {
return isDraftMode ? (
<CompensationsPreview
initialCompensations={initialCompensationsPage}
initialLocations={initialLocationsData}
initialCompanyInfo={initialCompanyInfo}
/>
) : (
<Compensations
compensations={initialCompensationsPage.data}
locations={initialLocationsData.data}
companyInfo={initialCompanyInfo.data}
/>
);
}
Expand Down
34 changes: 29 additions & 5 deletions src/compensations/Compensations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,34 @@ import {
minSalariesExaminationYear,
salariesFromLocation,
} from "./utils/salary";
import { CompanyLocation } from "studio/lib/payloads/companyDetails";
import {
CompanyInfo,
CompanyLocation,
} from "studio/lib/payloads/companyDetails";
import {
IOption,
RadioButtonGroup,
} from "src/components/forms/radioButtonGroup/RadioButtonGroup";
import YearlyBonuses from "./components/yearlyBonuses/YearlyBonuses";
import BenefitsByLocation from "./components/benefitsByLocation/BenefitsByLocation";
import { formatAsCurrency } from "src/utils/i18n";

interface CompensationsProps {
compensations: CompensationsPage;
locations: CompanyLocation[];
companyInfo: CompanyInfo;
}

interface SalaryCalculatorFormState {
examinationYear: number;
selectedDegree: Degree;
}

const Compensations = ({ compensations, locations }: CompensationsProps) => {
const Compensations = ({
compensations,
locations,
companyInfo,
}: CompensationsProps) => {
const [selectedLocation, setSelectedLocation] = useState<string>(
locations[0]._id,
);
Expand Down Expand Up @@ -119,19 +128,34 @@ const Compensations = ({ compensations, locations }: CompensationsProps) => {
/>
{salary !== null ? (
<div aria-live="polite">
<Text> Du vil få en årlig lønn på {salary}</Text>
<Text>
{" "}
Du vil få en årlig lønn på{" "}
{formatAsCurrency(
salary,
companyInfo.siteMetadata.locale,
companyInfo.siteMetadata.currency,
)}
</Text>
{compensations.pensionPercent && (
<Text>
Du vil få en årlig pensjon på omtrent{" "}
{calculatePension(salary, compensations.pensionPercent)}
{formatAsCurrency(
calculatePension(salary, compensations.pensionPercent),
companyInfo.siteMetadata.locale,
companyInfo.siteMetadata.currency,
)}
</Text>
)}
</div>
) : null}
</>
)}
{yearlyBonusesForLocation && (
<YearlyBonuses bonuses={yearlyBonusesForLocation} />
<YearlyBonuses
bonuses={yearlyBonusesForLocation}
companyInfo={companyInfo}
/>
)}
<BenefitsByLocation benefits={benefitsFilteredByLocation} />
</div>
Expand Down
25 changes: 21 additions & 4 deletions src/compensations/CompensationsPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@ import { Suspense } from "react";
import Compensations from "./Compensations";
import { QueryResponseInitial, useQuery } from "@sanity/react-loader";
import { CompensationsPage } from "studio/lib/payloads/compensations";
import { CompanyLocation } from "studio/lib/payloads/companyDetails";
import { COMPANY_LOCATIONS_QUERY } from "studio/lib/queries/companyDetails";
import {
CompanyInfo,
CompanyLocation,
} from "studio/lib/payloads/companyDetails";
import {
COMPANY_INFO_QUERY,
COMPANY_LOCATIONS_QUERY,
} from "studio/lib/queries/companyDetails";
import { COMPENSATIONS_PAGE_QUERY } from "studio/lib/queries/pages";

interface CompensationsPreviewProps {
initialCompensations: QueryResponseInitial<CompensationsPage>;
initialLocations: QueryResponseInitial<CompanyLocation[]>;
initialCompanyInfo: QueryResponseInitial<CompanyInfo>;
}

const CompensationsPreview = ({
initialCompensations,
initialLocations,
initialCompanyInfo,
}: CompensationsPreviewProps) => {
const { data } = useQuery<CompensationsPage>(
COMPENSATIONS_PAGE_QUERY,
Expand All @@ -27,10 +35,19 @@ const CompensationsPreview = ({
{ initial: initialLocations },
);

const { data: companyInfo } = useQuery<CompanyInfo>(COMPANY_INFO_QUERY, {
initial: initialCompanyInfo,
});

return (
locationData && (
locationData &&
companyInfo && (
<Suspense>
<Compensations compensations={data} locations={locationData} />
<Compensations
compensations={data}
locations={locationData}
companyInfo={companyInfo}
/>
</Suspense>
)
);
Expand Down
13 changes: 11 additions & 2 deletions src/compensations/components/yearlyBonuses/YearlyBonuses.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { BonusPage } from "studio/lib/payloads/compensations";
import Text from "../../../components/text/Text";
import styles from "./yearlyBonuses.module.css";
import { formatAsCurrency } from "../../../utils/i18n";
import { CompanyInfo } from "../../../../studio/lib/payloads/companyDetails";

interface YearlyBonusesProps {
bonuses: BonusPage[];
companyInfo: CompanyInfo;
}

const YearlyBonuses = ({ bonuses }: YearlyBonusesProps) => {
const YearlyBonuses = ({ bonuses, companyInfo }: YearlyBonusesProps) => {
return (
<div className={styles.wrapper}>
<Text type={"h3"}>Historisk bonus</Text>
Expand All @@ -30,7 +33,13 @@ const YearlyBonuses = ({ bonuses }: YearlyBonusesProps) => {
<Text type={"small"}>{bonus.year}</Text>
</th>
<td className={styles.bonusCell}>
<Text type={"small"}>{bonus.bonus}</Text>
<Text type={"small"}>
{formatAsCurrency(
bonus.bonus,
companyInfo.siteMetadata.locale,
companyInfo.siteMetadata.currency,
)}
</Text>
</td>
</tr>
))}
Expand Down
7 changes: 5 additions & 2 deletions src/components/navigation/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { linkID } from "studio/schemas/objects/link";
import primaryLogoFile from "../../stories/assets/energiai-primary-logo.svg";
import secondaryLogoFile from "../../stories/assets/energiai-secondary-logo.svg";
import { SocialMediaProfiles } from "studio/lib/payloads/socialMedia";
import { CompanyInfo } from "../../../studio/lib/payloads/companyDetails";

// Mock Navigation Data
export const mockNavigation: Navigation = {
Expand Down Expand Up @@ -109,10 +110,12 @@ export const mockNavigation: Navigation = {
],
};

export const mockCompanyInfo = {
export const mockCompanyInfo: CompanyInfo = {
siteMetadata: {
siteName: "Varaint",
siteName: "Variant",
defaultLanguage: "en",
locale: "nb-NO",
currency: "NOK",
},
brandAssets: {
primaryLogo: {
Expand Down
9 changes: 9 additions & 0 deletions src/utils/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function formatAsCurrency(
number: number,
locale: string,
currency: string,
) {
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
number,
);
}
2 changes: 2 additions & 0 deletions studio/lib/payloads/companyDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface BrandAssets {
interface SiteMetadata {
siteName: string;
defaultLanguage: string;
locale: string;
currency: string;
}

export interface CompanyLocation {
Expand Down
28 changes: 28 additions & 0 deletions studio/schemas/documents/companyInfo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { defineType, defineField } from "sanity";
import seo from "../objects/seo";
import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount";
import { relevantLocales } from "../../utils/locales";
import { relevantCurrencies } from "../../utils/currencies";

export const companyInfoID = "companyInfo";

Expand Down Expand Up @@ -43,6 +45,32 @@ const companyInfo = defineType({
],
},
}),
defineField({
name: "locale",
type: "string",
title: "Locale",
description: "Select the most relevant locale for the website.",
initialValue: relevantLocales[0].code,
options: {
list: relevantLocales.map(({ title, code }) => ({
title: `${title} (${code})`,
value: code,
})),
},
}),
defineField({
name: "currency",
type: "string",
title: "Currency",
description: "Select the most relevant currency for the website.",
initialValue: relevantCurrencies[0],
options: {
list: relevantCurrencies.map((currency) => ({
title: currency,
value: currency,
})),
},
}),
defineField({
name: "contactEmail",
type: "string",
Expand Down
10 changes: 10 additions & 0 deletions studio/utils/currencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// ISO 4217 currency codes (https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes)
export const relevantCurrencies = [
"NOK",
"SEK",
"EUR",
"GBP",
"USD",
"DKK",
"ISK",
];
39 changes: 39 additions & 0 deletions studio/utils/locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface Locale {
title: string;
code: string;
}

export const relevantLocales: Locale[] = [
{
title: "Norway",
code: "nb-NO",
},
{
title: "Norway",
code: "nn-NO",
},
{
title: "Sweden",
code: "sv-SE",
},
{
title: "United Kingdom",
code: "en-GB",
},
{
title: "United States",
code: "en-US",
},
{
title: "Denmark",
code: "da-DK",
},
{
title: "Finland",
code: "fi-FI",
},
{
title: "Iceland",
code: "is-IS",
},
];

0 comments on commit 4d3c9ca

Please sign in to comment.