From 2d57244528c108bb8c725322853813885c8fb294 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Sun, 1 Dec 2024 20:46:33 +0100 Subject: [PATCH 01/18] feat: adds sanity structure for compensation calculator section --- .../CompensationCalculator.tsx | 32 +++++++ .../compensation-calculator.module.css | 80 +++++++++++++++++ src/utils/renderSection.tsx | 3 + studio/lib/interfaces/pages.ts | 14 ++- studio/lib/queries/pages.ts | 7 ++ studio/lib/queries/specialPages.ts | 14 +++ studio/schemas/documents/pageBuilder.ts | 2 + .../sections/compensation-calculator.ts | 86 +++++++++++++++++++ 8 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 src/components/sections/compensation-calculator/CompensationCalculator.tsx create mode 100644 src/components/sections/compensation-calculator/compensation-calculator.module.css create mode 100644 studio/schemas/objects/sections/compensation-calculator.ts diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx new file mode 100644 index 000000000..f42f1e12c --- /dev/null +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -0,0 +1,32 @@ +import Text from "src/components/text/Text"; +import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; +import { COMPENSATIONS_BENEFITS } from "studio/lib/queries/specialPages"; +import { loadStudioQuery } from "studio/lib/store"; + +import styles from "./compensation-calculator.module.css"; + +export interface CompensationCalculatorProps { + language: string; + section: CompensationCalculatorSection; +} + +export default async function CompensationCalculator({ + language, + section, +}: CompensationCalculatorProps) { + const employeesPageRes = await loadStudioQuery<{ slug: string }>( + COMPENSATIONS_BENEFITS, + { + language, + }, + ); + const compensationCalculator = employeesPageRes.data.slug; + + return ( +
+
+ {section.moduleTitle} +
+
+ ); +} diff --git a/src/components/sections/compensation-calculator/compensation-calculator.module.css b/src/components/sections/compensation-calculator/compensation-calculator.module.css new file mode 100644 index 000000000..01a0e1ebf --- /dev/null +++ b/src/components/sections/compensation-calculator/compensation-calculator.module.css @@ -0,0 +1,80 @@ +/* Employees.tsx */ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + margin: 5rem 1rem; + flex-wrap: wrap; + + max-width: var(--max-content-width-large); + margin: 0 auto; + + background: var(--background-bg-light-secondary); + padding: 3rem; + border-radius: var(--radius-large, 24px); + + @media (max-width: 375px) { + padding: 1.5rem; + } +} + +.header { + color: var(--background-bg-light-primary); + font-size: 48px; + font-weight: 600; + align-self: flex-start; +} + +.employees { + display: flex; + flex-direction: column; + max-width: 1400px; + width: 100%; + text-wrap: wrap; + gap: 0.5rem; +} + +/* EmployeeList.tsx */ + +.employeeFiltersWrapper { + padding: 1.5rem 0; + align-self: flex-start; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.employeeFilterWrapper { + flex-wrap: wrap; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.employeeFilterLabel { + flex-shrink: 0; + @media (min-width: 375px) { + min-width: 4.5rem; + width: 4.5rem; + } +} + +.peopleCountWrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.employeeCount { + font-size: 16px; +} + +.employeeCountValue { + font-weight: 500; +} + +.peopleContainer { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1.5rem; +} diff --git a/src/utils/renderSection.tsx b/src/utils/renderSection.tsx index 1908e8fd9..55023a94d 100644 --- a/src/utils/renderSection.tsx +++ b/src/utils/renderSection.tsx @@ -6,6 +6,7 @@ import Callout from "src/components/sections/callout/Callout"; import CalloutPreview from "src/components/sections/callout/CalloutPreview"; import CallToAction from "src/components/sections/callToAction/CallToAction"; import CallToActionPreview from "src/components/sections/callToAction/CallToActionPreview"; +import CompensationCalculator from "src/components/sections/compensation-calculator/CompensationCalculator"; import ContactBox from "src/components/sections/contact-box/ContactBox"; import EmployeeHighlight from "src/components/sections/employeeHighlight/EmployeeHighlight"; import Employees from "src/components/sections/employees/Employees"; @@ -251,6 +252,8 @@ const SectionRenderer = ({ initialData={initialData} /> ); + case "compensationCalculator": + return ; case "grid": return renderGridSection(section, sectionIndex, isDraftMode, initialData); case "contactBox": diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 01c692109..fca74571c 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -116,6 +116,16 @@ export interface JobsSection { subtitle: string; } +export interface CompensationCalculatorSection { + _type: "compensationCalculator"; + _key: string; + moduleTitle: string; + calculatorTitle: string; + calculatorDescription: PortableTextBlock[]; + handbookTitle: string; + handbookDescription: PortableTextBlock[]; +} + export interface EmployeeHighlightSection { _type: "employeeHighlight"; _key: string; @@ -137,8 +147,8 @@ export type Section = | GridSection | ContactBoxSection | EmployeesSection - | JobsSection - | EmployeeHighlightSection; + | CompensationCalculatorSection + | JobsSection; export interface PageBuilder { _createdAt: string; diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 06bcf9d4e..7797125a6 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -43,6 +43,13 @@ const SECTIONS_FRAGMENT = groq` ${TRANSLATED_LINK_FRAGMENT} } }, + _type == "compensationCalculator" => { + "moduleTitle": ${translatedFieldFragment("moduleTitle")}, + "calculatorTitle": ${translatedFieldFragment("calculatorTitle")}, + "calculatorDesc": ${translatedFieldFragment("calculatorDesc")}, + "handbookTitle": ${translatedFieldFragment("handbookTitle")}, + "handbookDesc": ${translatedFieldFragment("handbookDesc")} + }, _type == "employees" => { "basicTitle": ${translatedFieldFragment("basicTitle")} }, diff --git a/studio/lib/queries/specialPages.ts b/studio/lib/queries/specialPages.ts index 3c12660b2..e5c401604 100644 --- a/studio/lib/queries/specialPages.ts +++ b/studio/lib/queries/specialPages.ts @@ -26,6 +26,20 @@ export const COMPENSATIONS_PAGE_BY_SLUG_QUERY = groq` }, } `; +export const COMPENSATIONS_BENEFITS = groq` + *[_type == "compensations"][0] { + ..., + ${LANGUAGE_FIELD_FRAGMENT}, + "benefitsByLocation": benefitsByLocation[] { + ..., + "benefits": benefits[] { + ..., + "basicTitle": ${translatedFieldFragment("basicTitle")}, + "richText": ${translatedFieldFragment("richText")} + } + }, + } +`; export const COMPENSATIONS_PAGE_SITEMAP_QUERY = groq` *[_type == "compensations"][0] { _updatedAt, diff --git a/studio/schemas/documents/pageBuilder.ts b/studio/schemas/documents/pageBuilder.ts index 855b3cc9f..3b5a220ce 100644 --- a/studio/schemas/documents/pageBuilder.ts +++ b/studio/schemas/documents/pageBuilder.ts @@ -5,6 +5,7 @@ import { titleID } from "studio/schemas/fields/text"; import article from "studio/schemas/objects/sections/article"; import callout from "studio/schemas/objects/sections/callout"; import callToAction from "studio/schemas/objects/sections/callToAction"; +import { compensationCalculator } from "studio/schemas/objects/sections/compensation-calculator"; import contactBox from "studio/schemas/objects/sections/contact-box"; import { employeeHighlightSection } from "studio/schemas/objects/sections/employeeHighlight"; import { employees } from "studio/schemas/objects/sections/employees"; @@ -57,6 +58,7 @@ const pageBuilder = defineType({ contactBox, jobs, employeeHighlightSection, + compensationCalculator, ], }), ], diff --git a/studio/schemas/objects/sections/compensation-calculator.ts b/studio/schemas/objects/sections/compensation-calculator.ts new file mode 100644 index 000000000..251e1c475 --- /dev/null +++ b/studio/schemas/objects/sections/compensation-calculator.ts @@ -0,0 +1,86 @@ +import { defineField } from "sanity"; + +export const compensationCalculatorId = "compensationCalculator"; + +export const compensationCalculator = defineField({ + name: compensationCalculatorId, + title: "Compensation Calculator", + type: "object", + fields: [ + { + name: "moduleTitle", + type: "internationalizedArrayString", + title: "Module Title", + description: "Title that will be displayed at the top of the section.", + initialValue: [ + { _key: "en", value: "Employee Experience" }, + { _key: "no", value: "Ansattopplevelsen" }, + ], + }, + { + name: "calculatorTitle", + type: "internationalizedArrayString", + title: "Calculator Title", + description: "Title that will be displayed inside the calculator.", + initialValue: [ + { _key: "en", value: "Salary Calculator" }, + { _key: "no", value: "Lønnskalkulator" }, + ], + }, + { + name: "calculatorDesc", + title: "Calculator Description", + type: "internationalizedArrayString", + description: "Description that will be displayed inside the calculator.", + initialValue: [ + { + _key: "en", + value: + "We believe that salary should be simple, open and predictable. Therefore, we designed a transparent salary model that equalizes all employees.", + }, + { + _key: "no", + value: + "Vi mener lønn bør være enkelt, åpent og forutsigbart. Derfor designet vi en transparent lønnsmodell som likestiller alle ansatte.", + }, + ], + }, + { + name: "handbookTitle", + type: "internationalizedArrayString", + title: "Handbook Title", + description: "Title that will be displayed inside the handbook section.", + initialValue: [ + { _key: "en", value: "Handbook" }, + { _key: "no", value: "Håndbok" }, + ], + }, + { + name: "handbookDesc", + title: "Handbook Description", + type: "internationalizedArrayString", + description: + "Description that will be displayed inside the handbook section.", + + initialValue: [ + { + _key: "en", + value: + "Words and actions should go hand in hand. All about us, rules and more you can find in the handbook. If we change, we change the handbook.", + }, + { + _key: "no", + value: + "Ord og handling bør gå hånd i hånd. Alt om oss, regler og finner du i håndboken. Endrer vi på oss, endrer vi håndboken.", + }, + ], + }, + ], + preview: { + prepare() { + return { + title: "Compensation Calculator", + }; + }, + }, +}); From ee5371e5839f2926294df22f8cc3a64c9809aa37 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Sun, 1 Dec 2024 20:49:10 +0100 Subject: [PATCH 02/18] linty --- .../sections/compensation-calculator/CompensationCalculator.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index f42f1e12c..dd98edcbb 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -21,6 +21,7 @@ export default async function CompensationCalculator({ }, ); const compensationCalculator = employeesPageRes.data.slug; + console.log(compensationCalculator); return (
From b77bf71139515ed541f1596bfde63e620f8b0bb2 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Mon, 2 Dec 2024 08:58:17 +0100 Subject: [PATCH 03/18] feat: adds option for violet/dark for salary calc --- .../CompensationCalculator.tsx | 43 ++++++--- .../compensation-calculator.module.css | 94 +++++-------------- src/styles/global.css | 2 + studio/lib/interfaces/pages.ts | 6 +- studio/lib/queries/pages.ts | 4 +- studio/lib/queries/specialPages.ts | 14 +-- .../sections/compensation-calculator.ts | 27 +++++- 7 files changed, 89 insertions(+), 101 deletions(-) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index dd98edcbb..bb354512c 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,7 +1,8 @@ import Text from "src/components/text/Text"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; -import { COMPENSATIONS_BENEFITS } from "studio/lib/queries/specialPages"; +import { COMPENSATIONS_SALARIES } from "studio/lib/queries/specialPages"; import { loadStudioQuery } from "studio/lib/store"; +import { SalariesByLocation } from "studio/lib/interfaces/compensations"; import styles from "./compensation-calculator.module.css"; @@ -14,19 +15,37 @@ export default async function CompensationCalculator({ language, section, }: CompensationCalculatorProps) { - const employeesPageRes = await loadStudioQuery<{ slug: string }>( - COMPENSATIONS_BENEFITS, - { - language, - }, - ); - const compensationCalculator = employeesPageRes.data.slug; - console.log(compensationCalculator); + const employeesPageRes = await loadStudioQuery<{ + slug: string; + salariesByLocation: SalariesByLocation[]; + }>(COMPENSATIONS_SALARIES, { + language, + }); + console.log(employeesPageRes.data); + + // TODO: add cn util or andIf + const calculatorBgClassname = + section.background === "violet" + ? `${styles.calculator} ${styles["calculator--violet"]}` + : styles.calculator; + const handbookBgClassname = + section.background === "violet" + ? `${styles.handbook} ${styles["handbook--violet"]}` + : styles.handbook; return ( -
-
- {section.moduleTitle} +
+ {section.moduleTitle} + +
+
+ {section.calculatorTitle} + {section.calculatorDescription} +
+
+ {section.handbookTitle} + {section.handbookDescription} +
); diff --git a/src/components/sections/compensation-calculator/compensation-calculator.module.css b/src/components/sections/compensation-calculator/compensation-calculator.module.css index 01a0e1ebf..3570c0223 100644 --- a/src/components/sections/compensation-calculator/compensation-calculator.module.css +++ b/src/components/sections/compensation-calculator/compensation-calculator.module.css @@ -1,80 +1,28 @@ -/* Employees.tsx */ -.wrapper { - display: flex; - flex-direction: column; - align-items: center; - margin: 5rem 1rem; - flex-wrap: wrap; - - max-width: var(--max-content-width-large); - margin: 0 auto; - - background: var(--background-bg-light-secondary); - padding: 3rem; - border-radius: var(--radius-large, 24px); - - @media (max-width: 375px) { - padding: 1.5rem; - } -} - -.header { - color: var(--background-bg-light-primary); - font-size: 48px; - font-weight: 600; - align-self: flex-start; -} - -.employees { - display: flex; - flex-direction: column; - max-width: 1400px; - width: 100%; - text-wrap: wrap; - gap: 0.5rem; -} - -/* EmployeeList.tsx */ - -.employeeFiltersWrapper { - padding: 1.5rem 0; - align-self: flex-start; - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.employeeFilterWrapper { - flex-wrap: wrap; - display: flex; - align-items: center; - gap: 0.75rem; +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 1rem; } -.employeeFilterLabel { - flex-shrink: 0; - @media (min-width: 375px) { - min-width: 4.5rem; - width: 4.5rem; - } -} +.calculator, +.handbook { + --_calcBg: var(--background-bg-light-secondary); + --_calcColor: var(--text-primary-dark); + padding: 3rem; + border-radius: var(--radius-small); -.peopleCountWrapper { - display: flex; - flex-direction: column; - gap: 0.5rem; + background-color: var(--_calcBg); + color: var(--_calcColor); } - -.employeeCount { - font-size: 16px; +.calculator { + --_calcBg: var(--background-bg-dark); + --_calcColor: var(--text-primary-light); } - -.employeeCountValue { - font-weight: 500; +.calculator--violet { + --_calcBg: var(--surface-violet-light); + --_calcColor: var(--text-primary-dark); } - -.peopleContainer { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 1.5rem; +.handbook--violet { + --_calcBg: var(--surface-violet); + --_calcColor: var(--text-primary-light); } diff --git a/src/styles/global.css b/src/styles/global.css index a41301a01..8dc52e95c 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -124,6 +124,8 @@ html { --surface-red-light: var(--Red-100); --surface-yellow-light: var(--Yellow-50); --surface-white: var(--Light-50); + --surface-violet-light: var(--Violet-50); + --surface-violet: var(--Violet-700); /* breakpoints */ --breakpoint-mobile: 425px; diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index fca74571c..7d489d93f 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -5,6 +5,7 @@ import { SeoData } from "src/utils/seo"; import { Slug } from "./global"; import { IImage, ImageExtendedProps } from "./media"; import { ILink } from "./navigation"; +import { CompensationCalculatorBackground } from "studio/schemas/objects/sections/compensation-calculator"; export interface HeroSection { _type: "hero"; @@ -120,10 +121,11 @@ export interface CompensationCalculatorSection { _type: "compensationCalculator"; _key: string; moduleTitle: string; + background: CompensationCalculatorBackground; calculatorTitle: string; - calculatorDescription: PortableTextBlock[]; + calculatorDescription: string; handbookTitle: string; - handbookDescription: PortableTextBlock[]; + handbookDescription: string; } export interface EmployeeHighlightSection { diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 7797125a6..e72622eb1 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -46,9 +46,9 @@ const SECTIONS_FRAGMENT = groq` _type == "compensationCalculator" => { "moduleTitle": ${translatedFieldFragment("moduleTitle")}, "calculatorTitle": ${translatedFieldFragment("calculatorTitle")}, - "calculatorDesc": ${translatedFieldFragment("calculatorDesc")}, + "calculatorDescription": ${translatedFieldFragment("calculatorDescription")}, "handbookTitle": ${translatedFieldFragment("handbookTitle")}, - "handbookDesc": ${translatedFieldFragment("handbookDesc")} + "handbookDescription": ${translatedFieldFragment("handbookDescription")} }, _type == "employees" => { "basicTitle": ${translatedFieldFragment("basicTitle")} diff --git a/studio/lib/queries/specialPages.ts b/studio/lib/queries/specialPages.ts index e5c401604..781a4eb90 100644 --- a/studio/lib/queries/specialPages.ts +++ b/studio/lib/queries/specialPages.ts @@ -26,17 +26,11 @@ export const COMPENSATIONS_PAGE_BY_SLUG_QUERY = groq` }, } `; -export const COMPENSATIONS_BENEFITS = groq` +export const COMPENSATIONS_SALARIES = groq` *[_type == "compensations"][0] { - ..., - ${LANGUAGE_FIELD_FRAGMENT}, - "benefitsByLocation": benefitsByLocation[] { - ..., - "benefits": benefits[] { - ..., - "basicTitle": ${translatedFieldFragment("basicTitle")}, - "richText": ${translatedFieldFragment("richText")} - } + "slug": ${translatedFieldFragment("slug")}, + "salariesByLocation": salariesByLocation[] { + ... }, } `; diff --git a/studio/schemas/objects/sections/compensation-calculator.ts b/studio/schemas/objects/sections/compensation-calculator.ts index 251e1c475..8997cc5fa 100644 --- a/studio/schemas/objects/sections/compensation-calculator.ts +++ b/studio/schemas/objects/sections/compensation-calculator.ts @@ -2,6 +2,16 @@ import { defineField } from "sanity"; export const compensationCalculatorId = "compensationCalculator"; +export enum CompensationCalculatorBackground { + Dark = "dark", + Violet = "violet", +} + +const backgroundOptions = [ + { title: "Dark", value: CompensationCalculatorBackground.Dark }, + { title: "Violet", value: CompensationCalculatorBackground.Violet }, +]; + export const compensationCalculator = defineField({ name: compensationCalculatorId, title: "Compensation Calculator", @@ -17,6 +27,19 @@ export const compensationCalculator = defineField({ { _key: "no", value: "Ansattopplevelsen" }, ], }, + { + name: "background", + title: "Background", + type: "string", + description: + "Select the whether the calculator should be purple or have dark background.", + options: { + list: backgroundOptions, + layout: "radio", + }, + initialValue: CompensationCalculatorBackground.Dark, + }, + { name: "calculatorTitle", type: "internationalizedArrayString", @@ -28,7 +51,7 @@ export const compensationCalculator = defineField({ ], }, { - name: "calculatorDesc", + name: "calculatorDescription", title: "Calculator Description", type: "internationalizedArrayString", description: "Description that will be displayed inside the calculator.", @@ -56,7 +79,7 @@ export const compensationCalculator = defineField({ ], }, { - name: "handbookDesc", + name: "handbookDescription", title: "Handbook Description", type: "internationalizedArrayString", description: From 39cb46f0b3b10ca7e94d7377e8f35bfcaf228fb5 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Mon, 2 Dec 2024 09:00:10 +0100 Subject: [PATCH 04/18] linty --- .../sections/compensation-calculator/CompensationCalculator.tsx | 2 +- studio/lib/interfaces/pages.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index bb354512c..052ae7128 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,8 +1,8 @@ import Text from "src/components/text/Text"; +import { SalariesByLocation } from "studio/lib/interfaces/compensations"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; import { COMPENSATIONS_SALARIES } from "studio/lib/queries/specialPages"; import { loadStudioQuery } from "studio/lib/store"; -import { SalariesByLocation } from "studio/lib/interfaces/compensations"; import styles from "./compensation-calculator.module.css"; diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 7d489d93f..92164d430 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -1,11 +1,11 @@ import { PortableTextBlock } from "sanity"; import { SeoData } from "src/utils/seo"; +import { CompensationCalculatorBackground } from "studio/schemas/objects/sections/compensation-calculator"; import { Slug } from "./global"; import { IImage, ImageExtendedProps } from "./media"; import { ILink } from "./navigation"; -import { CompensationCalculatorBackground } from "studio/schemas/objects/sections/compensation-calculator"; export interface HeroSection { _type: "hero"; From fb31b2d0ea179af66f58fc6cf6520b61e535f52d Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Tue, 3 Dec 2024 08:25:31 +0100 Subject: [PATCH 05/18] temp --- .../compensation-calculator/Calculator.tsx | 140 ++++++++++++++++++ .../CompensationCalculator.tsx | 43 +++++- .../compensation-calculator/actions.ts | 29 ++++ .../sections/compensation-calculator/types.ts | 6 + studio/lib/queries/specialPages.ts | 9 ++ 5 files changed, 219 insertions(+), 8 deletions(-) create mode 100644 src/components/sections/compensation-calculator/Calculator.tsx create mode 100644 src/components/sections/compensation-calculator/actions.ts create mode 100644 src/components/sections/compensation-calculator/types.ts diff --git a/src/components/sections/compensation-calculator/Calculator.tsx b/src/components/sections/compensation-calculator/Calculator.tsx new file mode 100644 index 000000000..4190de525 --- /dev/null +++ b/src/components/sections/compensation-calculator/Calculator.tsx @@ -0,0 +1,140 @@ +"use client"; + +import { use, useEffect, useOptimistic, useState } from "react"; +import Text from "src/components/text/Text"; +import { formatAsCurrency } from "src/utils/i18n"; +import { LocaleDocument } from "studio/lib/interfaces/locale"; +import { getSalaryByYear } from "./actions"; +import { + IOption, + RadioButtonGroup, +} from "src/components/forms/radioButtonGroup/RadioButtonGroup"; +import InputField from "src/components/forms/inputField/InputField"; +import Button from "src/components/buttons/Button"; + +export type Degree = "bachelor" | "master"; + +type CalculatorProps = { + initialSalary: number; + localeRes: Promise; +}; + +type SalarySettings = { + year: number; + degree: Degree; +}; + +export default function Calculator({ + localeRes, + initialSalary, +}: CalculatorProps) { + const locale = use(localeRes); + const [year, setYear] = useState(2024); + const [degree, setDegree] = useState("bachelor"); + const [salary, setSalary] = useState(initialSalary); + + if (!locale) { + console.error( + "[CompensationCalculator]: Sanity data not found. Not rendering CompensationCalculator.", + ); + return null; + } + + useEffect(() => { + async function fetchSalary() { + const salaryByYear = await getSalaryByYear(year); + console.log(salaryByYear); + + setSalary(salaryByYear.ok ? salaryByYear.value : initialSalary); + } + + fetchSalary(); + }, [year, degree]); + + return ( +
+ { + const salaryByYear = await getSalaryByYear(2024); + + console.log(salaryByYear); + }} + /> + + {salary !== null ? ( +
+ Din årslønn + + {salary} + {/* {formatAsCurrency(salary, locale.locale, locale.currency)} */} + + + bonus +
+ ) : null} +
+ ); +} + +const degreeOptions: IOption[] = [ + { id: "bachelor", label: "Bachelor" }, + { id: "master", label: "Master" }, +]; + +interface SalaryCalculatorProps { + examinationYearValue: number; + minExaminationYear: number; + maxExaminationYear: number; + selectedDegree: Degree; + onDegreeChanged: (degree: Degree) => void; + onExaminationYearChanged: (examinationYear: number) => void; + action: (data: FormData) => void; +} + +function SalaryCalculator({ + examinationYearValue: yearValue, + minExaminationYear, + maxExaminationYear, + selectedDegree, + onDegreeChanged, + onExaminationYearChanged, + action, +}: SalaryCalculatorProps) { + return ( +
+ + onDegreeChanged(selectedOption.id as Degree) + } + /> +
+ onExaminationYearChanged(parseInt(value))} + required + /> +
+ + + ); +} diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 052ae7128..7e486bcb7 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,10 +1,16 @@ import Text from "src/components/text/Text"; -import { SalariesByLocation } from "studio/lib/interfaces/compensations"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; import { COMPENSATIONS_SALARIES } from "studio/lib/queries/specialPages"; import { loadStudioQuery } from "studio/lib/store"; +import { Suspense } from "react"; +import { LocaleDocument } from "studio/lib/interfaces/locale"; +import { LOCALE_QUERY } from "studio/lib/queries/locale"; +import Calculator from "./Calculator"; +import { CompensationData } from "./types"; + import styles from "./compensation-calculator.module.css"; +import { getSalaryByYear } from "./actions"; export interface CompensationCalculatorProps { language: string; @@ -15,13 +21,18 @@ export default async function CompensationCalculator({ language, section, }: CompensationCalculatorProps) { - const employeesPageRes = await loadStudioQuery<{ - slug: string; - salariesByLocation: SalariesByLocation[]; - }>(COMPENSATIONS_SALARIES, { - language, - }); - console.log(employeesPageRes.data); + const compensationsSalariesRes = loadStudioQuery( + COMPENSATIONS_SALARIES, + { + language, + }, + ).then((d) => d.data); + + const localeRes = loadStudioQuery(LOCALE_QUERY).then( + (d) => d.data, + ); + + const data = await getSalaryByYear(2024); // TODO: add cn util or andIf const calculatorBgClassname = @@ -33,6 +44,13 @@ export default async function CompensationCalculator({ ? `${styles.handbook} ${styles["handbook--violet"]}` : styles.handbook; + if (!data.ok) { + console.error( + "[CompensationCalculator]: Failed to get salary data for year 2024", + ); + return null; + } + return (
{section.moduleTitle} @@ -41,6 +59,15 @@ export default async function CompensationCalculator({
{section.calculatorTitle} {section.calculatorDescription} + + Loading...
}> + +
{section.handbookTitle} diff --git a/src/components/sections/compensation-calculator/actions.ts b/src/components/sections/compensation-calculator/actions.ts new file mode 100644 index 000000000..4f7968c0f --- /dev/null +++ b/src/components/sections/compensation-calculator/actions.ts @@ -0,0 +1,29 @@ +"use server"; + +import { isSalariesType } from "studio/components/salariesInput/utils/parseSalaries"; +import { COMPENSATIONS_SALARY_BY_YEAR } from "studio/lib/queries/specialPages"; +import { loadStudioQuery } from "studio/lib/store"; +import { Result, ResultError, ResultOk } from "studio/utils/result"; + +export async function getSalaryByYear( + year: number, +): Promise> { + const res = await loadStudioQuery<{ + salariesByLocation: { yearlySalaries: { salaries: string } }; + }>(COMPENSATIONS_SALARY_BY_YEAR, { + year, + }); + + try { + const parsedSalaries = JSON.parse( + res.data.salariesByLocation.yearlySalaries.salaries, + ); + if (!isSalariesType(parsedSalaries) || !parsedSalaries[year]) { + return ResultError("Parsed salaries data was not valid"); + } + + return ResultOk(parsedSalaries[year] as number); + } catch (e) { + return ResultError("Parsed salaries data was not valid"); + } +} diff --git a/src/components/sections/compensation-calculator/types.ts b/src/components/sections/compensation-calculator/types.ts new file mode 100644 index 000000000..a46f59e63 --- /dev/null +++ b/src/components/sections/compensation-calculator/types.ts @@ -0,0 +1,6 @@ +import { SalariesByLocation } from "studio/lib/interfaces/compensations"; + +export type CompensationData = { + slug: string; + salariesByLocation: SalariesByLocation[]; +}; diff --git a/studio/lib/queries/specialPages.ts b/studio/lib/queries/specialPages.ts index 781a4eb90..2fc37ad38 100644 --- a/studio/lib/queries/specialPages.ts +++ b/studio/lib/queries/specialPages.ts @@ -34,6 +34,15 @@ export const COMPENSATIONS_SALARIES = groq` }, } `; +export const COMPENSATIONS_SALARY_BY_YEAR = groq` + *[_type == "compensations"][0] { + "salariesByLocation": salariesByLocation[0] { + "yearlySalaries": yearlySalaries[0] { + ... + } + }, + } +`; export const COMPENSATIONS_PAGE_SITEMAP_QUERY = groq` *[_type == "compensations"][0] { _updatedAt, From 402e755dd456c42443d00ce80bf543b72d810371 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Tue, 3 Dec 2024 13:12:36 +0100 Subject: [PATCH 06/18] fix: make calculator use state to be quicker --- src/components/compensations/utils/salary.ts | 2 +- .../compensation-calculator/Calculator.tsx | 58 ++++++++----------- .../CompensationCalculator.tsx | 36 +++--------- .../{actions.ts => api.ts} | 26 ++++++--- .../sections/compensation-calculator/types.ts | 3 + 5 files changed, 54 insertions(+), 71 deletions(-) rename src/components/sections/compensation-calculator/{actions.ts => api.ts} (71%) diff --git a/src/components/compensations/utils/salary.ts b/src/components/compensations/utils/salary.ts index 9e747e4a1..c4899a825 100644 --- a/src/components/compensations/utils/salary.ts +++ b/src/components/compensations/utils/salary.ts @@ -11,7 +11,7 @@ export function calculateSalary( salaries: Salaries, ): number | undefined { const degreeValue = degree === "bachelor" ? 1 : 0; - const adjustedYear = examinationYear + degreeValue; + const adjustedYear = examinationYear - degreeValue; return salaries[adjustedYear]; } diff --git a/src/components/sections/compensation-calculator/Calculator.tsx b/src/components/sections/compensation-calculator/Calculator.tsx index 4190de525..48774db29 100644 --- a/src/components/sections/compensation-calculator/Calculator.tsx +++ b/src/components/sections/compensation-calculator/Calculator.tsx @@ -1,55 +1,48 @@ "use client"; -import { use, useEffect, useOptimistic, useState } from "react"; -import Text from "src/components/text/Text"; -import { formatAsCurrency } from "src/utils/i18n"; -import { LocaleDocument } from "studio/lib/interfaces/locale"; -import { getSalaryByYear } from "./actions"; +import { use, useState } from "react"; + +import Button from "src/components/buttons/Button"; +import { calculateSalary } from "src/components/compensations/utils/salary"; +import InputField from "src/components/forms/inputField/InputField"; import { IOption, RadioButtonGroup, } from "src/components/forms/radioButtonGroup/RadioButtonGroup"; -import InputField from "src/components/forms/inputField/InputField"; -import Button from "src/components/buttons/Button"; +import Text from "src/components/text/Text"; +import { formatAsCurrency } from "src/utils/i18n"; +import { LocaleDocument } from "studio/lib/interfaces/locale"; +import { Result } from "studio/utils/result"; -export type Degree = "bachelor" | "master"; +import { Degree, SalaryData } from "./types"; type CalculatorProps = { - initialSalary: number; localeRes: Promise; -}; + salariesRes: Promise>; -type SalarySettings = { - year: number; - degree: Degree; + initialDegree: Degree; + initialYear: number; }; export default function Calculator({ localeRes, - initialSalary, + salariesRes, + initialDegree, + initialYear, }: CalculatorProps) { const locale = use(localeRes); - const [year, setYear] = useState(2024); - const [degree, setDegree] = useState("bachelor"); - const [salary, setSalary] = useState(initialSalary); + const salaries = use(salariesRes); + const [year, setYear] = useState(initialYear); + const [degree, setDegree] = useState(initialDegree); - if (!locale) { + if (!locale || !salaries.ok) { console.error( "[CompensationCalculator]: Sanity data not found. Not rendering CompensationCalculator.", ); return null; } - useEffect(() => { - async function fetchSalary() { - const salaryByYear = await getSalaryByYear(year); - console.log(salaryByYear); - - setSalary(salaryByYear.ok ? salaryByYear.value : initialSalary); - } - - fetchSalary(); - }, [year, degree]); + const salary = calculateSalary(year, degree, salaries.value) ?? 0; return (
@@ -60,19 +53,14 @@ export default function Calculator({ selectedDegree={degree} onDegreeChanged={setDegree} onExaminationYearChanged={setYear} - action={async () => { - const salaryByYear = await getSalaryByYear(2024); - - console.log(salaryByYear); - }} + action={async () => {}} /> {salary !== null ? (
Din årslønn - {salary} - {/* {formatAsCurrency(salary, locale.locale, locale.currency)} */} + {formatAsCurrency(salary, locale.locale, locale.currency)} + bonus
diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 7e486bcb7..09bd200e4 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,16 +1,14 @@ +import { Suspense } from "react"; + import Text from "src/components/text/Text"; +import { LocaleDocument } from "studio/lib/interfaces/locale"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; -import { COMPENSATIONS_SALARIES } from "studio/lib/queries/specialPages"; +import { LOCALE_QUERY } from "studio/lib/queries/locale"; import { loadStudioQuery } from "studio/lib/store"; -import { Suspense } from "react"; -import { LocaleDocument } from "studio/lib/interfaces/locale"; -import { LOCALE_QUERY } from "studio/lib/queries/locale"; +import { getSalaryByYear } from "./api"; import Calculator from "./Calculator"; -import { CompensationData } from "./types"; - import styles from "./compensation-calculator.module.css"; -import { getSalaryByYear } from "./actions"; export interface CompensationCalculatorProps { language: string; @@ -18,22 +16,13 @@ export interface CompensationCalculatorProps { } export default async function CompensationCalculator({ - language, section, }: CompensationCalculatorProps) { - const compensationsSalariesRes = loadStudioQuery( - COMPENSATIONS_SALARIES, - { - language, - }, - ).then((d) => d.data); - + const salariesRes = getSalaryByYear(2024); const localeRes = loadStudioQuery(LOCALE_QUERY).then( (d) => d.data, ); - const data = await getSalaryByYear(2024); - // TODO: add cn util or andIf const calculatorBgClassname = section.background === "violet" @@ -44,13 +33,6 @@ export default async function CompensationCalculator({ ? `${styles.handbook} ${styles["handbook--violet"]}` : styles.handbook; - if (!data.ok) { - console.error( - "[CompensationCalculator]: Failed to get salary data for year 2024", - ); - return null; - } - return (
{section.moduleTitle} @@ -63,9 +45,9 @@ export default async function CompensationCalculator({ Loading...
}>
diff --git a/src/components/sections/compensation-calculator/actions.ts b/src/components/sections/compensation-calculator/api.ts similarity index 71% rename from src/components/sections/compensation-calculator/actions.ts rename to src/components/sections/compensation-calculator/api.ts index 4f7968c0f..5713d04fa 100644 --- a/src/components/sections/compensation-calculator/actions.ts +++ b/src/components/sections/compensation-calculator/api.ts @@ -1,29 +1,39 @@ -"use server"; - import { isSalariesType } from "studio/components/salariesInput/utils/parseSalaries"; import { COMPENSATIONS_SALARY_BY_YEAR } from "studio/lib/queries/specialPages"; import { loadStudioQuery } from "studio/lib/store"; import { Result, ResultError, ResultOk } from "studio/utils/result"; +import { SalaryData } from "./types"; + export async function getSalaryByYear( year: number, -): Promise> { +): Promise> { const res = await loadStudioQuery<{ salariesByLocation: { yearlySalaries: { salaries: string } }; - }>(COMPENSATIONS_SALARY_BY_YEAR, { - year, - }); + }>( + COMPENSATIONS_SALARY_BY_YEAR, + { + year, + }, + { + cache: "force-cache", + next: { + revalidate: 60 * 60 * 24 * 120, + }, + }, + ); try { const parsedSalaries = JSON.parse( res.data.salariesByLocation.yearlySalaries.salaries, ); + if (!isSalariesType(parsedSalaries) || !parsedSalaries[year]) { return ResultError("Parsed salaries data was not valid"); } - return ResultOk(parsedSalaries[year] as number); - } catch (e) { + return ResultOk(parsedSalaries); + } catch { return ResultError("Parsed salaries data was not valid"); } } diff --git a/src/components/sections/compensation-calculator/types.ts b/src/components/sections/compensation-calculator/types.ts index a46f59e63..31ece1dc5 100644 --- a/src/components/sections/compensation-calculator/types.ts +++ b/src/components/sections/compensation-calculator/types.ts @@ -4,3 +4,6 @@ export type CompensationData = { slug: string; salariesByLocation: SalariesByLocation[]; }; +export type Degree = "bachelor" | "master"; + +export type SalaryData = Record; From 3347d7e530e01363a3c122767a631aaffb599d21 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Tue, 3 Dec 2024 15:18:55 +0100 Subject: [PATCH 07/18] fix: structure compensation data better --- .../CompensationCalculator.tsx | 28 ++++- .../sections/compensation-calculator/api.ts | 3 + studio/lib/interfaces/pages.ts | 16 ++- studio/lib/queries/pages.ts | 16 ++- studio/lib/queries/specialPages.ts | 4 + .../sections/compensation-calculator.ts | 117 +++++++++++------- 6 files changed, 127 insertions(+), 57 deletions(-) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 09bd200e4..9f97fc505 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,5 +1,6 @@ import { Suspense } from "react"; +import LinkButton from "src/components/linkButton/LinkButton"; import Text from "src/components/text/Text"; import { LocaleDocument } from "studio/lib/interfaces/locale"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; @@ -17,12 +18,15 @@ export interface CompensationCalculatorProps { export default async function CompensationCalculator({ section, + language, }: CompensationCalculatorProps) { - const salariesRes = getSalaryByYear(2024); + const salariesRes = getSalaryByYear(2024, language); const localeRes = loadStudioQuery(LOCALE_QUERY).then( (d) => d.data, ); + console.log("DATA", section); + // TODO: add cn util or andIf const calculatorBgClassname = section.background === "violet" @@ -39,21 +43,33 @@ export default async function CompensationCalculator({
- {section.calculatorTitle} - {section.calculatorDescription} + {section.calculatorBlock.calculatorTitle} + + {section.calculatorBlock.calculatorDescription} + Loading...
}> + + {section.calculatorBlock.calculatorLink && ( + + )}
- {section.handbookTitle} - {section.handbookDescription} + {section.handbookBlock.handbookTitle} + + {section.handbookBlock.handbookDescription} + + + {section.handbookBlock.handbookLink && ( + + )}
diff --git a/src/components/sections/compensation-calculator/api.ts b/src/components/sections/compensation-calculator/api.ts index 5713d04fa..1f1f1f5e1 100644 --- a/src/components/sections/compensation-calculator/api.ts +++ b/src/components/sections/compensation-calculator/api.ts @@ -7,13 +7,16 @@ import { SalaryData } from "./types"; export async function getSalaryByYear( year: number, + language: string, ): Promise> { const res = await loadStudioQuery<{ + slug: string; salariesByLocation: { yearlySalaries: { salaries: string } }; }>( COMPENSATIONS_SALARY_BY_YEAR, { year, + language, }, { cache: "force-cache", diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 92164d430..f3a1284dc 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -122,10 +122,18 @@ export interface CompensationCalculatorSection { _key: string; moduleTitle: string; background: CompensationCalculatorBackground; - calculatorTitle: string; - calculatorDescription: string; - handbookTitle: string; - handbookDescription: string; + + calculatorBlock: { + calculatorTitle: string; + calculatorDescription: string; + calculatorLink: ILink; + }; + + handbookBlock: { + handbookTitle: string; + handbookDescription: string; + handbookLink: ILink; + }; } export interface EmployeeHighlightSection { diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index e72622eb1..d22808d0e 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -44,11 +44,19 @@ const SECTIONS_FRAGMENT = groq` } }, _type == "compensationCalculator" => { + ..., "moduleTitle": ${translatedFieldFragment("moduleTitle")}, - "calculatorTitle": ${translatedFieldFragment("calculatorTitle")}, - "calculatorDescription": ${translatedFieldFragment("calculatorDescription")}, - "handbookTitle": ${translatedFieldFragment("handbookTitle")}, - "handbookDescription": ${translatedFieldFragment("handbookDescription")} + + "calculatorBlock": calculatorBlock { + ..., + "calculatorTitle": ${translatedFieldFragment("calculatorTitle")}, + "calculatorDescription": ${translatedFieldFragment("calculatorDescription")}, + }, + "handbookBlock": handbookBlock { + ..., + "handbookTitle": ${translatedFieldFragment("handbookTitle")}, + "handbookDescription": ${translatedFieldFragment("handbookDescription")} + } }, _type == "employees" => { "basicTitle": ${translatedFieldFragment("basicTitle")} diff --git a/studio/lib/queries/specialPages.ts b/studio/lib/queries/specialPages.ts index 2fc37ad38..54bc831c4 100644 --- a/studio/lib/queries/specialPages.ts +++ b/studio/lib/queries/specialPages.ts @@ -34,8 +34,12 @@ export const COMPENSATIONS_SALARIES = groq` }, } `; + +// Just select the first location and the first year.. +// @TODO: make this a bit more robust. export const COMPENSATIONS_SALARY_BY_YEAR = groq` *[_type == "compensations"][0] { + "slug": ${translatedFieldFragment("slug")}, "salariesByLocation": salariesByLocation[0] { "yearlySalaries": yearlySalaries[0] { ... diff --git a/studio/schemas/objects/sections/compensation-calculator.ts b/studio/schemas/objects/sections/compensation-calculator.ts index 8997cc5fa..5388ed185 100644 --- a/studio/schemas/objects/sections/compensation-calculator.ts +++ b/studio/schemas/objects/sections/compensation-calculator.ts @@ -1,5 +1,7 @@ import { defineField } from "sanity"; +import { link } from "studio/schemas/objects/link"; + export const compensationCalculatorId = "compensationCalculator"; export enum CompensationCalculatorBackground { @@ -27,6 +29,7 @@ export const compensationCalculator = defineField({ { _key: "no", value: "Ansattopplevelsen" }, ], }, + { name: "background", title: "Background", @@ -41,60 +44,88 @@ export const compensationCalculator = defineField({ }, { - name: "calculatorTitle", - type: "internationalizedArrayString", - title: "Calculator Title", - description: "Title that will be displayed inside the calculator.", - initialValue: [ - { _key: "en", value: "Salary Calculator" }, - { _key: "no", value: "Lønnskalkulator" }, - ], - }, - { - name: "calculatorDescription", - title: "Calculator Description", - type: "internationalizedArrayString", - description: "Description that will be displayed inside the calculator.", - initialValue: [ + name: "calculatorBlock", + type: "object", + + fields: [ { - _key: "en", - value: - "We believe that salary should be simple, open and predictable. Therefore, we designed a transparent salary model that equalizes all employees.", + name: "calculatorTitle", + type: "internationalizedArrayString", + title: "Calculator Title", + description: "Title that will be displayed inside the calculator.", + initialValue: [ + { _key: "en", value: "Salary Calculator" }, + { _key: "no", value: "Lønnskalkulator" }, + ], }, + { - _key: "no", - value: - "Vi mener lønn bør være enkelt, åpent og forutsigbart. Derfor designet vi en transparent lønnsmodell som likestiller alle ansatte.", + name: "calculatorDescription", + title: "Calculator Description", + type: "internationalizedArrayString", + description: + "Description that will be displayed inside the calculator.", + initialValue: [ + { + _key: "en", + value: + "We believe that salary should be simple, open and predictable. Therefore, we designed a transparent salary model that equalizes all employees.", + }, + { + _key: "no", + value: + "Vi mener lønn bør være enkelt, åpent og forutsigbart. Derfor designet vi en transparent lønnsmodell som likestiller alle ansatte.", + }, + ], + }, + + { + ...link, + name: "calculatorLink", }, ], }, + { - name: "handbookTitle", - type: "internationalizedArrayString", - title: "Handbook Title", - description: "Title that will be displayed inside the handbook section.", - initialValue: [ - { _key: "en", value: "Handbook" }, - { _key: "no", value: "Håndbok" }, - ], - }, - { - name: "handbookDescription", - title: "Handbook Description", - type: "internationalizedArrayString", - description: - "Description that will be displayed inside the handbook section.", + name: "handbookBlock", + type: "object", - initialValue: [ + fields: [ + { + name: "handbookTitle", + type: "internationalizedArrayString", + title: "Handbook Title", + description: + "Title that will be displayed inside the handbook section.", + initialValue: [ + { _key: "en", value: "Handbook" }, + { _key: "no", value: "Håndbok" }, + ], + }, { - _key: "en", - value: - "Words and actions should go hand in hand. All about us, rules and more you can find in the handbook. If we change, we change the handbook.", + name: "handbookDescription", + title: "Handbook Description", + type: "internationalizedArrayString", + description: + "Description that will be displayed inside the handbook section.", + + initialValue: [ + { + _key: "en", + value: + "Words and actions should go hand in hand. All about us, rules and more you can find in the handbook. If we change, we change the handbook.", + }, + { + _key: "no", + value: + "Ord og handling bør gå hånd i hånd. Alt om oss, regler og finner du i håndboken. Endrer vi på oss, endrer vi håndboken.", + }, + ], }, + { - _key: "no", - value: - "Ord og handling bør gå hånd i hånd. Alt om oss, regler og finner du i håndboken. Endrer vi på oss, endrer vi håndboken.", + ...link, + name: "handbookLink", }, ], }, From 90fb68c7cb0fb98905022215827693530bf69801 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Tue, 3 Dec 2024 16:57:06 +0100 Subject: [PATCH 08/18] removes logging --- .../sections/compensation-calculator/CompensationCalculator.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 9f97fc505..21d2735c7 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -25,8 +25,6 @@ export default async function CompensationCalculator({ (d) => d.data, ); - console.log("DATA", section); - // TODO: add cn util or andIf const calculatorBgClassname = section.background === "violet" From 65b24894fb06343c6bbfd3f6bfa86d50769c1a97 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Wed, 4 Dec 2024 10:42:45 +0100 Subject: [PATCH 09/18] feat: adds general handbook links to compensation site --- .../compensation-calculator/Calculator.tsx | 4 ---- .../CompensationCalculator.tsx | 11 ++++++++- .../sections/compensation-calculator/api.ts | 23 ++++++++++++++++++- studio/lib/interfaces/compensations.ts | 3 +++ studio/lib/queries/specialPages.ts | 13 +++++++++-- studio/schemas/documents/compensations.ts | 11 +++++++++ 6 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/components/sections/compensation-calculator/Calculator.tsx b/src/components/sections/compensation-calculator/Calculator.tsx index 48774db29..e9d7d3575 100644 --- a/src/components/sections/compensation-calculator/Calculator.tsx +++ b/src/components/sections/compensation-calculator/Calculator.tsx @@ -2,7 +2,6 @@ import { use, useState } from "react"; -import Button from "src/components/buttons/Button"; import { calculateSalary } from "src/components/compensations/utils/salary"; import InputField from "src/components/forms/inputField/InputField"; import { @@ -120,9 +119,6 @@ function SalaryCalculator({ required />
- ); } diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 21d2735c7..3c5ba38c8 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -7,7 +7,7 @@ import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; import { LOCALE_QUERY } from "studio/lib/queries/locale"; import { loadStudioQuery } from "studio/lib/store"; -import { getSalaryByYear } from "./api"; +import { getHandbookLinksFromCompensationPage, getSalaryByYear } from "./api"; import Calculator from "./Calculator"; import styles from "./compensation-calculator.module.css"; @@ -24,6 +24,7 @@ export default async function CompensationCalculator({ const localeRes = loadStudioQuery(LOCALE_QUERY).then( (d) => d.data, ); + const handbookLinksRes = await getHandbookLinksFromCompensationPage(language); // TODO: add cn util or andIf const calculatorBgClassname = @@ -65,6 +66,14 @@ export default async function CompensationCalculator({ {section.handbookBlock.handbookDescription} + {handbookLinksRes.ok && ( +
+ {handbookLinksRes.value.map((link) => ( + + ))} +
+ )} + {section.handbookBlock.handbookLink && ( )} diff --git a/src/components/sections/compensation-calculator/api.ts b/src/components/sections/compensation-calculator/api.ts index 1f1f1f5e1..30ed9ac2c 100644 --- a/src/components/sections/compensation-calculator/api.ts +++ b/src/components/sections/compensation-calculator/api.ts @@ -1,10 +1,31 @@ import { isSalariesType } from "studio/components/salariesInput/utils/parseSalaries"; -import { COMPENSATIONS_SALARY_BY_YEAR } from "studio/lib/queries/specialPages"; +import { ILink } from "studio/lib/interfaces/navigation"; +import { + COMPENSATIONS_HANDBOOK_LINKS, + COMPENSATIONS_SALARY_BY_YEAR, +} from "studio/lib/queries/specialPages"; import { loadStudioQuery } from "studio/lib/store"; import { Result, ResultError, ResultOk } from "studio/utils/result"; import { SalaryData } from "./types"; +export async function getHandbookLinksFromCompensationPage( + language: string, +): Promise> { + const res = await loadStudioQuery<{ handbookLinks: ILink[] }>( + COMPENSATIONS_HANDBOOK_LINKS, + { language }, + { + cache: "force-cache", + next: { + revalidate: 60 * 60, + }, + }, + ); + + return ResultOk(res.data.handbookLinks); +} + export async function getSalaryByYear( year: number, language: string, diff --git a/studio/lib/interfaces/compensations.ts b/studio/lib/interfaces/compensations.ts index c5527afbd..b9cfd8e6f 100644 --- a/studio/lib/interfaces/compensations.ts +++ b/studio/lib/interfaces/compensations.ts @@ -2,6 +2,8 @@ import { PortableTextBlock, Reference } from "sanity"; import { SeoData } from "src/utils/seo"; +import { ILink } from "./navigation"; + export interface Benefit { _type: string; _key: string; @@ -86,5 +88,6 @@ export interface CompensationsPage { bonusesByLocation: BonusesByLocationPage[]; salariesByLocation: SalariesByLocation[]; showSalaryCalculator: boolean; + handbookLinks: ILink[]; seo: SeoData; } diff --git a/studio/lib/queries/specialPages.ts b/studio/lib/queries/specialPages.ts index 54bc831c4..0cd9b4fd2 100644 --- a/studio/lib/queries/specialPages.ts +++ b/studio/lib/queries/specialPages.ts @@ -1,6 +1,6 @@ import { groq } from "next-sanity"; -import { LANGUAGE_FIELD_FRAGMENT } from "./i18n"; +import { LANGUAGE_FIELD_FRAGMENT, TRANSLATED_LINK_FRAGMENT } from "./i18n"; import { translatedFieldFragment } from "./utils/i18n"; //Compensations @@ -44,9 +44,18 @@ export const COMPENSATIONS_SALARY_BY_YEAR = groq` "yearlySalaries": yearlySalaries[0] { ... } - }, + } + } +`; +export const COMPENSATIONS_HANDBOOK_LINKS = groq` + *[_type == "compensations"][0] { + "handbookLinks": handbookLinks[] { + ..., + ${TRANSLATED_LINK_FRAGMENT} + } } `; + export const COMPENSATIONS_PAGE_SITEMAP_QUERY = groq` *[_type == "compensations"][0] { _updatedAt, diff --git a/studio/schemas/documents/compensations.ts b/studio/schemas/documents/compensations.ts index 7aa300b1b..0f4f91bef 100644 --- a/studio/schemas/documents/compensations.ts +++ b/studio/schemas/documents/compensations.ts @@ -6,6 +6,7 @@ import { benefitsByLocation } from "studio/schemas/objects/compensations/benefit import { bonusesByLocation } from "studio/schemas/objects/compensations/bonusesByLocation"; import { pensionPercent } from "studio/schemas/objects/compensations/pension"; import { salariesByLocation } from "studio/schemas/objects/compensations/salariesByLocation"; +import { link } from "studio/schemas/objects/link"; import { titleSlug } from "studio/schemas/schemaTypes/slug"; import { firstTranslation } from "studio/utils/i18n"; @@ -39,6 +40,16 @@ const compensations = defineType({ bonusesByLocation, benefitsByLocation, salariesByLocation, + + defineField({ + name: "handbookLinks", + title: "Handbook Section Links", + description: + "Ordered links to sections in handbook to show important information when it comes to benefints. Used by compensation calculator module.", + type: "array", + of: [link], + }), + { name: "seo", type: "internationalizedArraySeo", From ae46f62c2cdd615f3196537e93627aada26f4e3a Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Wed, 4 Dec 2024 13:51:27 +0100 Subject: [PATCH 10/18] feat: adds bottom link to compensation block --- .../CompensationCalculator.tsx | 26 ++++++++++++++++-- .../compensation-calculator.module.css | 27 +++++++++++++++++++ studio/lib/queries/pages.ts | 10 ++++++- .../sections/compensation-calculator.ts | 4 +++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/components/sections/compensation-calculator/CompensationCalculator.tsx b/src/components/sections/compensation-calculator/CompensationCalculator.tsx index 3c5ba38c8..61bd68ec8 100644 --- a/src/components/sections/compensation-calculator/CompensationCalculator.tsx +++ b/src/components/sections/compensation-calculator/CompensationCalculator.tsx @@ -1,7 +1,9 @@ +import Link from "next/link"; import { Suspense } from "react"; import LinkButton from "src/components/linkButton/LinkButton"; import Text from "src/components/text/Text"; +import { getHref } from "src/utils/link"; import { LocaleDocument } from "studio/lib/interfaces/locale"; import { CompensationCalculatorSection } from "studio/lib/interfaces/pages"; import { LOCALE_QUERY } from "studio/lib/queries/locale"; @@ -59,6 +61,15 @@ export default async function CompensationCalculator({ {section.calculatorBlock.calculatorLink && ( )} + + {section.calculatorBlock.calculatorLink && ( +
+ +
+ )}
{section.handbookBlock.handbookTitle} @@ -69,13 +80,24 @@ export default async function CompensationCalculator({ {handbookLinksRes.ok && (
{handbookLinksRes.value.map((link) => ( - + + {link.linkTitle} + ))}
)} {section.handbookBlock.handbookLink && ( - +
+ +
)}
diff --git a/src/components/sections/compensation-calculator/compensation-calculator.module.css b/src/components/sections/compensation-calculator/compensation-calculator.module.css index 3570c0223..43a16a8ef 100644 --- a/src/components/sections/compensation-calculator/compensation-calculator.module.css +++ b/src/components/sections/compensation-calculator/compensation-calculator.module.css @@ -13,6 +13,10 @@ background-color: var(--_calcBg); color: var(--_calcColor); + + display: flex; + flex-direction: column; + gap: 1rem; } .calculator { --_calcBg: var(--background-bg-dark); @@ -26,3 +30,26 @@ --_calcBg: var(--surface-violet); --_calcColor: var(--text-primary-light); } + +.calculatorLink, +.handbookLink { + margin-top: auto; +} + +.handbookLinks { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +.handbookLink { + font-size: 2rem; + color: currentColor; +} +.handbookLink:hover { + text-decoration: underline; +} +.handbookLink:not(:last-child)::after { + content: "·"; + margin-left: 0.5rem; +} diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index d22808d0e..bbcf09e03 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -51,11 +51,19 @@ const SECTIONS_FRAGMENT = groq` ..., "calculatorTitle": ${translatedFieldFragment("calculatorTitle")}, "calculatorDescription": ${translatedFieldFragment("calculatorDescription")}, + "calculatorLink": calculatorLink { + ..., + ${TRANSLATED_LINK_FRAGMENT} + } }, "handbookBlock": handbookBlock { ..., "handbookTitle": ${translatedFieldFragment("handbookTitle")}, - "handbookDescription": ${translatedFieldFragment("handbookDescription")} + "handbookDescription": ${translatedFieldFragment("handbookDescription")}, + "handbookLink": handbookLink { + ..., + ${TRANSLATED_LINK_FRAGMENT} + } } }, _type == "employees" => { diff --git a/studio/schemas/objects/sections/compensation-calculator.ts b/studio/schemas/objects/sections/compensation-calculator.ts index 5388ed185..2acaeb38f 100644 --- a/studio/schemas/objects/sections/compensation-calculator.ts +++ b/studio/schemas/objects/sections/compensation-calculator.ts @@ -82,6 +82,8 @@ export const compensationCalculator = defineField({ { ...link, name: "calculatorLink", + description: + "Bottom link that will be displayed inside the handbook section.", }, ], }, @@ -126,6 +128,8 @@ export const compensationCalculator = defineField({ { ...link, name: "handbookLink", + description: + "Bottom link that will be displayed inside the handbook section.", }, ], }, From 1f14aa2b3b19815821bcc61e5ddd1efc672733a0 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Thu, 5 Dec 2024 16:26:28 +0100 Subject: [PATCH 11/18] fix: design for calculator component --- .../forms/inputField/inputField.module.css | 8 +- .../radioButtonGroup/RadioButtonGroup.tsx | 51 ++++++---- .../radioButtonGroup.module.css | 62 +++++------- src/components/linkButton/LinkButton.tsx | 29 ++++-- .../linkButton/linkButton.module.css | 19 +++- .../compensation-calculator/Calculator.tsx | 94 ++++++------------- .../CompensationCalculator.tsx | 41 ++++---- .../compensation-calculator.module.css | 58 +++++++++++- src/components/tag/index.tsx | 12 ++- studio/lib/interfaces/pages.ts | 1 + 10 files changed, 220 insertions(+), 155 deletions(-) diff --git a/src/components/forms/inputField/inputField.module.css b/src/components/forms/inputField/inputField.module.css index bc43b41ae..16f0c3a0d 100644 --- a/src/components/forms/inputField/inputField.module.css +++ b/src/components/forms/inputField/inputField.module.css @@ -8,7 +8,7 @@ } .label { - color: var(--background-bg-dark); + color: currentColor; } .input { @@ -16,10 +16,10 @@ padding: 0.5rem 0.75rem; align-items: center; gap: 0.5rem; - align-self: stretch; + font-size: 1rem; - border-radius: 0.5rem; - border: 1px solid var(--background-bg-light-secondary, #ece1d3); + border-radius: var(--radius-small); + border: 1px solid var(--stroke-primary, #ece1d3); background: var(--text-primary-light, #fff); } diff --git a/src/components/forms/radioButtonGroup/RadioButtonGroup.tsx b/src/components/forms/radioButtonGroup/RadioButtonGroup.tsx index c43071bca..fd203e74d 100644 --- a/src/components/forms/radioButtonGroup/RadioButtonGroup.tsx +++ b/src/components/forms/radioButtonGroup/RadioButtonGroup.tsx @@ -1,7 +1,8 @@ import React from "react"; import styles from "src/components/forms/radioButtonGroup/radioButtonGroup.module.css"; -import textStyles from "src/components/text/text.module.css"; +import { tagComponentStyle } from "src/components/tag"; +import Text from "src/components/text/Text"; export interface IOption { id: string; @@ -17,6 +18,8 @@ interface RadioButtonProps { checked?: boolean; disabled?: boolean; defaultChecked?: boolean; + background?: "light" | "dark" | "violet"; + onChange?: (event: React.ChangeEvent) => void; } @@ -26,6 +29,7 @@ interface RadioButtonGroupProps { options: IOption[]; selectedId: string; onValueChange: (option: IOption) => void; + background?: "light" | "dark" | "violet"; } export const RadioButtonGroup = ({ @@ -34,6 +38,7 @@ export const RadioButtonGroup = ({ options, selectedId, onValueChange, + background = "light", }: RadioButtonGroupProps) => { const onChange = (e: React.ChangeEvent) => { const selectedOption = options.find( @@ -46,21 +51,28 @@ export const RadioButtonGroup = ({ return (
- {label} -
- {options.map(({ id, label, disabled }) => ( - - ))} -
+ + + {label} + + + + {label} + + + {options.map(({ id, label, disabled }) => ( + + ))}
); }; @@ -70,15 +82,18 @@ const RadioButton = ({ name, value, label, - checked, + checked = false, disabled, defaultChecked, onChange, + background = "light", }: RadioButtonProps) => { + const className = tagComponentStyle(checked, background); + return (