Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3 - show salary for selected location, degree and examination year (web) #617

Merged
merged 3 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions src/compensations/Compensations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import { CompensationsPage } from "studio/lib/payloads/compensations";
import SalaryCalculator, {
Degree,
} from "./components/salaryCalculator/SalaryCalculator";
import { useState } from "react";
import { calculatePension, calculateSalary } from "./utils/calculateSalary";
import { useMemo, useState } from "react";
import {
calculatePension,
calculateSalary,
maxSalariesExaminationYear,
minSalariesExaminationYear,
salariesFromLocation,
} from "./utils/salary";
import { CompanyLocation } from "studio/lib/payloads/companyDetails";
import {
IOption,
Expand Down Expand Up @@ -36,6 +42,16 @@ const Compensations = ({ compensations, locations }: CompensationsProps) => {
selectedDegree: "bachelor",
});

const currentYearSalariesResult = useMemo(
() =>
salariesFromLocation(
currentYear,
selectedLocation,
compensations.salaries,
),
[currentYear, selectedLocation, compensations.salaries],
);

const updateSelectedDegree = (newDegree: Degree) => {
christinaroise marked this conversation as resolved.
Show resolved Hide resolved
setFormState((prevState) => ({
...prevState,
Expand All @@ -52,13 +68,14 @@ const Compensations = ({ compensations, locations }: CompensationsProps) => {

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
setSalary(
calculateSalary(
currentYear,
formState.examinationYear,
formState.selectedDegree,
),
if (!currentYearSalariesResult.ok) return;
const salary = calculateSalary(
formState.examinationYear,
formState.selectedDegree,
currentYearSalariesResult.value,
);
if (salary === undefined) return;
setSalary(salary);
};

const locationOptions: IOption[] = locations.map((companyLocation) => ({
Expand All @@ -85,10 +102,16 @@ const Compensations = ({ compensations, locations }: CompensationsProps) => {
selectedId={selectedLocation}
onValueChange={(option) => setSelectedLocation(option.id)}
/>
{compensations.showSalaryCalculator && (
{compensations.showSalaryCalculator && currentYearSalariesResult.ok && (
<>
<SalaryCalculator
examinationYearValue={formState.examinationYear}
minExaminationYear={minSalariesExaminationYear(
currentYearSalariesResult.value,
)}
maxExaminationYear={
maxSalariesExaminationYear(currentYearSalariesResult.value) - 1
}
selectedDegree={formState.selectedDegree}
onDegreeChanged={updateSelectedDegree}
onExaminationYearChanged={updateExaminationYear}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
RadioButtonGroup,
} from "src/components/forms/radioButtonGroup/RadioButtonGroup";
import Button from "src/components/buttons/Button";
import { maxExperience } from "src/compensations/utils/calculateSalary";

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

Expand All @@ -16,6 +15,8 @@ const degreeOptions: IOption[] = [

interface SalaryCalculatorProps {
examinationYearValue: number;
minExaminationYear: number;
maxExaminationYear: number;
selectedDegree: Degree;
onDegreeChanged: (degree: Degree) => void;
onExaminationYearChanged: (examinationYear: number) => void;
Expand All @@ -24,15 +25,13 @@ interface SalaryCalculatorProps {

export default function SalaryCalculator({
examinationYearValue: yearValue,
minExaminationYear,
maxExaminationYear,
selectedDegree,
onDegreeChanged,
onExaminationYearChanged,
onSubmit,
}: SalaryCalculatorProps) {
const currentYear = new Date().getFullYear();
const minExaminationYear = maxExperience(currentYear);
const maxExaminationYear = currentYear - 1;

return (
<form
//TODO: replace aria-label with static translation from Sanity
Expand Down
31 changes: 0 additions & 31 deletions src/compensations/utils/calculateSalary.ts

This file was deleted.

71 changes: 71 additions & 0 deletions src/compensations/utils/salary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
isSalariesType,
Salaries,
} from "studio/components/salariesInput/utils/parseSalaries";
import { SalariesByLocation } from "../../../studio/lib/payloads/compensations";
import { Result, ResultError, ResultOk } from "../../../studio/utils/result";

export function calculateSalary(
examinationYear: number,
degree: string,
salaries: Salaries,
): number | undefined {
const degreeValue = degree === "bachelor" ? 1 : 0;
const adjustedYear = examinationYear + degreeValue;
return salaries[adjustedYear];
}

export function calculatePension(
salary: number,
pensionPercent: number,
): number {
return Math.round(salary * (pensionPercent / 100));
}

function examinationYearsFromSalaries(salaries: Salaries) {
return Object.keys(salaries).map(Number);
}

export function minSalariesExaminationYear(salaries: Salaries): number {
return Math.min(...examinationYearsFromSalaries(salaries));
}

export function maxSalariesExaminationYear(salaries: Salaries): number {
return Math.max(...examinationYearsFromSalaries(salaries));
}

/**
* Retrieves salary data for a specific location and year.
*
* @param year - The year for which to retrieve salary data.
* @param locationId - The string from the `_ref` property of a location reference used in the salaries object.
* @param salariesByLocation - An array of salary data organized by location.
* @returns A Result containing either the Salaries object or an error message.
*
* @remarks
* This function searches for salary data based on the provided location and year.
* The locationId should match the _ref property of a location reference in the salariesByLocation array.
* If the data is found and valid, it returns a ResultOk with the parsed Salaries object.
* If the data is not found or invalid, it returns a ResultError with an appropriate error message.
*/
export function salariesFromLocation(
year: number,
locationId: string,
mathiazom marked this conversation as resolved.
Show resolved Hide resolved
salariesByLocation: SalariesByLocation[],
): Result<Salaries, string> {
const yearlySalaries = salariesByLocation.find(
(salary) => salary.location._ref === locationId,
)?.yearlySalaries;
if (yearlySalaries === undefined) {
return ResultError(`Could not find salaries for location '${locationId}'`);
}
const salariesData = yearlySalaries.find((s) => s.year === year);
if (salariesData === undefined) {
return ResultError(`Could not find salaries for year ${year}`);
}
const parsedSalaries = JSON.parse(salariesData.salaries);
if (!isSalariesType(parsedSalaries)) {
return ResultError("Parsed salaries data was not valid");
}
return ResultOk(parsedSalaries);
}
40 changes: 0 additions & 40 deletions src/compensations/utils/salaryData.ts

This file was deleted.

10 changes: 10 additions & 0 deletions studio/components/salariesInput/utils/parseSalaries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ export interface Salaries {
[year: string]: number;
}

export function isSalariesType(value: unknown): value is Salaries {
return (
typeof value === "object" &&
value !== null &&
Object.entries(value).every(
([k, v]) => !isNaN(Number(k)) && typeof v === "number",
)
);
}

const NON_EMPTY_DIGITS_ONLY_REGEX = new RegExp(/^\d+$/);
export const VALID_SALARY_REGEX = NON_EMPTY_DIGITS_ONLY_REGEX;

Expand Down
8 changes: 8 additions & 0 deletions studio/lib/payloads/compensations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export interface BonusPage {
bonus: number;
}

export interface SalariesByLocation {
_key: string;
_type: string;
location: Reference;
yearlySalaries: SalariesPage[];
}

export interface CompensationsPage {
_createdAt: string;
_id: string;
Expand All @@ -47,5 +54,6 @@ export interface CompensationsPage {
pensionPercent?: number;
benefitsByLocation: BenefitsByLocation[];
bonusesByLocation: BonusesByLocationPage[];
salaries: SalariesByLocation[];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we're doing salaries here, but benefitsByLocation above. Let's do either or

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That requires a breaking schema change, do we want to do it in this PR?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No not at all. We can do it in a cleanup?

showSalaryCalculator: boolean;
}