Skip to content

Commit

Permalink
feat: add salary calculator based on legacy site
Browse files Browse the repository at this point in the history
Co-authored-by: Ane <[email protected]>
  • Loading branch information
mathiazom and anemne committed Aug 23, 2024
1 parent 617eff3 commit ca17d29
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Please provide a brief summary of the changes you’ve made. Explain the purpose

## Visual Overview (Image/Video)

If applicable, please include screenshots or a short video showcasing the changes you’ve made.
If applicable, please include screenshots or a short video showcasing the changes you have made.

*Insert images or videos here.*

Expand Down
35 changes: 24 additions & 11 deletions src/app/(main)/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Metadata } from "next";
import { redirect } from "next/navigation";
import { Blog } from "src/blog/Blog";
import BlogPreview from "src/blog/BlogPreview";
import SalaryAndBenefits from 'src/salaryAndBenefits/SalaryAndBenefits';
import SalaryAndBenefits from "src/salaryAndBenefits/SalaryAndBenefits";
import { getDraftModeInfo } from "src/utils/draftmode";
import SectionRenderer from "src/utils/renderSection";
import { fetchSeoData, generateMetadataFromSeo } from "src/utils/seo";
import { BlogPage, PageBuilder, Post } from "studio/lib/payloads/pages";
import { SalaryAndBenefits as SalaryAndBenefitsPayload } from 'studio/lib/payloads/salaryAndBenefits';
import { SalaryAndBenefitsPage } from "studio/lib/payloads/salaryAndBenefits";
import {
BLOG_PAGE_QUERY,
POSTS_QUERY,
Expand Down Expand Up @@ -36,13 +36,22 @@ async function Page({ params }: Props) {
const { slug } = params;
const { perspective, isDraftMode } = getDraftModeInfo();

const [initialPage, initialBlogPage, initialSalaryAndBenefitsPage] = await Promise.all([
loadQuery<PageBuilder>(SLUG_QUERY, { slug }, { perspective }),
loadQuery<BlogPage>(BLOG_PAGE_QUERY, { slug }, { perspective }),
loadQuery<SalaryAndBenefitsPayload>(SALARY_AND_BENEFITS_PAGE_QUERY, { slug }, { perspective }),
]);
const [initialPage, initialBlogPage, initialSalaryAndBenefitsPage] =
await Promise.all([
loadQuery<PageBuilder>(SLUG_QUERY, { slug }, { perspective }),
loadQuery<BlogPage>(BLOG_PAGE_QUERY, { slug }, { perspective }),
loadQuery<SalaryAndBenefitsPage>(
SALARY_AND_BENEFITS_PAGE_QUERY,
{ slug },
{ perspective },
),
]);

if (!initialPage.data && !initialBlogPage.data && !initialSalaryAndBenefitsPage.data) {
if (
!initialPage.data &&
!initialBlogPage.data &&
!initialSalaryAndBenefitsPage.data
) {
console.log(`Page ${slug} not found`);
// TODO: add error snackbar
redirect("/");
Expand All @@ -53,7 +62,7 @@ async function Page({ params }: Props) {
const initialPosts = await loadQuery<Post[]>(
POSTS_QUERY,
{ slug },
{ perspective }
{ perspective },
);

if (!initialPosts) {
Expand Down Expand Up @@ -95,9 +104,13 @@ async function Page({ params }: Props) {

if (initialSalaryAndBenefitsPage.data) {
return isDraftMode ? (
<SalaryAndBenefitsPreview initialSalaryAndBenefits={initialSalaryAndBenefitsPage} />
<SalaryAndBenefitsPreview
initialSalaryAndBenefits={initialSalaryAndBenefitsPage}
/>
) : (
<SalaryAndBenefits salaryAndBenefits={initialSalaryAndBenefitsPage.data} />
<SalaryAndBenefits
salaryAndBenefits={initialSalaryAndBenefitsPage.data}
/>
);
}

Expand Down
8 changes: 7 additions & 1 deletion src/components/forms/inputField/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ interface InputFieldProps {
autoComplete?: HTMLInputAutoCompleteAttribute;
autoCorrect?: string;
type?: HTMLInputTypeAttribute;
max?: number;
min?: number;
spellCheck?: "true" | "false";
autoCapitalize?: string;
value: string;
value: string | number;
onChange: (name: string, value: string) => void;
required?: boolean;
}
Expand All @@ -24,6 +26,8 @@ const InputField = ({
autoComplete,
autoCorrect = "off",
type = "text",
max,
min,
spellCheck,
autoCapitalize,
value,
Expand Down Expand Up @@ -56,6 +60,8 @@ const InputField = ({
autoComplete={autoComplete}
autoCorrect={autoCorrect}
type={type}
max={max}
min={min}
className={styles.input}
spellCheck={spellCheck}
value={value}
Expand Down
14 changes: 7 additions & 7 deletions src/components/forms/radioButtonGroup/RadioButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import styles from "src/components/forms/radioButtonGroup/radioButtonGroup.modul
import { RadioButton } from "./components/RadioButton";
import textStyles from "src/components/text/text.module.css";

interface IOption {
export interface IOption {
id: string;
label: string;
disabled: boolean;
currentChecked: boolean;
disabled?: boolean;
currentSelected: boolean;
}

interface RenderOptionsProps {
Expand Down Expand Up @@ -41,8 +41,8 @@ interface RadioButtonGroupProps {
*
* ```
* const options = [
* { id: 'radio1', label: 'Option 1', value: '1', currentChecked: false },
* { id: 'radio2', label: 'Option 2', value: '2', currentChecked: true },
* { id: 'radio1', label: 'Option 1', value: '1', currentSelected: false },
* { id: 'radio2', label: 'Option 2', value: '2', currentSelected: true },
* ];
*
* <RadioButtonGroup
Expand Down Expand Up @@ -82,15 +82,15 @@ export const RadioButtonGroup = ({
const RenderOptions = ({ options, onChange }: RenderOptionsProps) => {
return (
<>
{options.map(({ id, label, disabled, currentChecked }) => (
{options.map(({ id, label, disabled, currentSelected }) => (
<RadioButton
key={id}
id={id}
label={label}
name="radio"
disabled={disabled}
value={label}
defaultChecked={currentChecked}
defaultChecked={currentSelected}
onChange={onChange}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.fieldset{
border: 0 none;
padding: 0;
}

.wrapper {
Expand Down
89 changes: 81 additions & 8 deletions src/salaryAndBenefits/SalaryAndBenefits.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,96 @@
"use client";

import styles from "./salaryAndBenefits.module.css";
import Text from "src/components/text/Text";
import {SalaryAndBenefits as SalaryAndBenefitsPayload} from "studio/lib/payloads/salaryAndBenefits";
import Benefit from './components/benefit/Benefit';
import SalaryCalculator, {
Degree,
} from "./components/salaryCalculator/SalaryCalculator";
import { useState } from "react";
import {
calculatePension,
calculateSalary,
maxExperience,
} from "./utils/calculateSalary";
import { SalaryAndBenefitsPage } from "studio/lib/payloads/salaryAndBenefits";
import { RichText } from "src/components/richText/RichText";

interface SalaryAndBenefitsProps {
salaryAndBenefits: SalaryAndBenefitsPayload
salaryAndBenefits: SalaryAndBenefitsPage;
}

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

const SalaryAndBenefits = ({salaryAndBenefits}: SalaryAndBenefitsProps) => {
const SalaryAndBenefits = ({ salaryAndBenefits }: SalaryAndBenefitsProps) => {
const currentYear = new Date().getFullYear();
const minExaminationYear = maxExperience(currentYear);
const maxExaminationYear = currentYear - 1;

const [formState, setFormState] = useState<SalaryCalculatorFormState>({
examinationYear: currentYear - 1,
selectedDegree: "bachelor",
});
const [salary, setSalary] = useState<number | null>(null);

const updateSelectedDegree = (newDegree: Degree) => {
setFormState((prevState) => ({
...prevState,
selectedDegree: newDegree,
}));
};

const updateExaminationYear = (newYear: number) => {
setFormState((prevState) => ({
...prevState,
examinationYear: newYear,
}));
};

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
setSalary(
calculateSalary(
currentYear,
formState.examinationYear,
formState.selectedDegree,
),
);
};

return (
<div className={styles.wrapper}>
<Text type="h1">{salaryAndBenefits.basicTitle}</Text>
{salaryAndBenefits.showSalaryCalculator && (
<SalaryCalculator
// TODO: should also take in degree state (this requires changes to IOption of RadioButtonGroup)
examinationYear={formState.examinationYear}
minExaminationYear={minExaminationYear}
maxExaminationYear={maxExaminationYear}
onDegreeChanged={updateSelectedDegree}
onExaminationYearChanged={updateExaminationYear}
onSubmit={handleSubmit}
/>
)}
{salary !== null ? (
<div aria-live="polite">
<Text> Du vil få en årlig lønn på {salary}</Text>
<Text>
Du vil få en årlig pensjon på omtrent {calculatePension(salary)}
</Text>
</div>
) : null}
<div className={styles.benefits}>
{salaryAndBenefits.benefits.map((benefit) => (
<Benefit key={benefit._key} benefit={benefit} />
<div key={benefit._key} className={styles.benefitWrapper}>
<Text type="h2">{benefit.basicTitle}</Text>
<RichText value={benefit.richText} />
</div>
))}
</div>
</div>
)
}
);
};

export default SalaryAndBenefits;
export default SalaryAndBenefits;
6 changes: 3 additions & 3 deletions src/salaryAndBenefits/SalaryAndBenefitsPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
import { Suspense } from "react";
import SalaryAndBenefits from "./SalaryAndBenefits";
import { QueryResponseInitial, useQuery } from "@sanity/react-loader";
import { SalaryAndBenefits as SalaryAndBenefitsPayload } from "studio/lib/payloads/salaryAndBenefits";
import { SalaryAndBenefitsPage } from "studio/lib/payloads/salaryAndBenefits";
import { SALARY_AND_BENEFITS_PAGE_QUERY } from "studio/lib/queries/pages";

interface SalaryAndBenefitsPreviewProps {
initialSalaryAndBenefits: QueryResponseInitial<SalaryAndBenefitsPayload>;
initialSalaryAndBenefits: QueryResponseInitial<SalaryAndBenefitsPage>;
}

const SalaryAndBenefitsPreview = ({
initialSalaryAndBenefits,
}: SalaryAndBenefitsPreviewProps) => {
const { data: newData } = useQuery<SalaryAndBenefitsPayload>(
const { data: newData } = useQuery<SalaryAndBenefitsPage>(
SALARY_AND_BENEFITS_PAGE_QUERY,
{ slug: initialSalaryAndBenefits.data.slug.current },
{ initial: initialSalaryAndBenefits },
Expand Down
18 changes: 0 additions & 18 deletions src/salaryAndBenefits/components/benefit/Benefit.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import styles from "./salaryCalculator.module.css";
import InputField from "src/components/forms/inputField/InputField";
import {
IOption,
RadioButtonGroup,
} from "src/components/forms/radioButtonGroup/RadioButtonGroup";
import Button from "src/components/buttons/Button";

export type Degree = "bachelor" | "master";

const degreeOptions: IOption[] = [
{
id: "bachelor",
label: "Bachelor",
currentSelected: true,
disabled: false,
},
{ id: "master", label: "Master", currentSelected: false, disabled: false },
];

interface SalaryCalculatorProps {
examinationYear: number;
minExaminationYear: number;
maxExaminationYear: number;
onDegreeChanged: (degree: Degree) => void;
onExaminationYearChanged: (examinationYear: number) => void;
onSubmit: (event: React.FormEvent) => void;
}

export default function SalaryCalculator({
examinationYear,
minExaminationYear,
maxExaminationYear,
onDegreeChanged,
onExaminationYearChanged,
onSubmit,
}: SalaryCalculatorProps) {
return (
<form
aria-label="salary calculator"
className={styles.calculator}
onSubmit={onSubmit}
>
<RadioButtonGroup
id="degree-group"
label="Choose your degree"
options={degreeOptions}
onValueChange={(value) =>
(value.id === "bachelor" || value.id === "master") &&
onDegreeChanged(value.id)
}
/>
<InputField
label="year"
name="examinationYear"
type="number"
max={maxExaminationYear}
min={minExaminationYear}
value={examinationYear}
onChange={(_name, value) => onExaminationYearChanged(parseInt(value))}
required
/>
<Button type="secondary" size="small">
Submit
</Button>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.wrapper {
.calculator{
display: flex;
flex-direction: column;
gap: 1rem;
gap: 2rem;
}
6 changes: 6 additions & 0 deletions src/salaryAndBenefits/salaryAndBenefits.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@
flex-direction: column;
gap: 5rem;
max-width: var(--max-content-width-medium);
}

.benefitWrapper {
display: flex;
flex-direction: column;
gap: 1rem;
}
Loading

0 comments on commit ca17d29

Please sign in to comment.