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

cleanup compensation and add the start of bonus #569

Merged
merged 10 commits into from
Sep 4, 2024
4 changes: 2 additions & 2 deletions studio/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import posts from "./schemas/documents/post";
import categories from "./schemas/fields/categories";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

for a better differentiation between i.e benefits.locations and companyLocations I renamed this part

import legalDocument from "./schemas/documents/legalDocuments";
import benefit from "./schemas/documents/benefit";
import location from "./schemas/documents/location";
import companyLocations from "./schemas/documents/companyLocations";
import compensations from "./schemas/documents/compensations";
import salaryAndBenefits from "./schemas/documents/salaryAndBenefits";
import siteSettings from "./schemas/documents/siteSettings";
Expand All @@ -35,6 +35,6 @@ export const schema: { types: SchemaTypeDefinition[] } = {
compensations,
salaryAndBenefits,
benefit,
location,
companyLocations,
],
};
14 changes: 11 additions & 3 deletions studio/schemas/deskStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { soMeLinksID } from "./documents/socialMediaProfiles";
import { companyInfoID } from "./documents/companyInfo";
import { legalDocumentID } from "./documents/legalDocuments";
import { compensationsId } from "./documents/compensations";
import { companyLocationsID } from "./documents/companyLocations";

export default (S: StructureBuilder) =>
S.list()
Expand All @@ -39,9 +40,13 @@ export default (S: StructureBuilder) =>
.title("Company Information"),
),
S.listItem()
.title("Locations")
.title("Company Locations")
.icon(PinIcon)
.child(S.documentTypeList("location").title("Locations")),
.child(
S.documentTypeList(companyLocationsID).title(
"Company Locations",
),
),
]),
),
S.listItem()
Expand Down Expand Up @@ -85,7 +90,10 @@ export default (S: StructureBuilder) =>
.title("Compensations")
.icon(HeartIcon)
.child(
S.documentTypeList(compensationsId).title("Compensations"),
S.document()
.schemaType(compensationsId)
.documentId(compensationsId)
.title("Compensations"),
),
]),
),
Expand Down
20 changes: 20 additions & 0 deletions studio/schemas/documents/companyLocations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineField } from "sanity";

export const companyLocationsID = "companyLocations";
export const companyLocationID = "companyLocation";

const companyLocations = defineField({
name: companyLocationsID,
type: "document",
title: "Location",
description: "Content related to an individual location within the company",
fields: [
defineField({
name: companyLocationID,
type: "string",
title: "Location",
}),
],
});

export default companyLocations;
96 changes: 39 additions & 57 deletions studio/schemas/documents/compensations.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { defineField, defineType } from "sanity";
import { titleSlug } from "../schemaTypes/slug";
import seo from "../objects/seo";
import locations from "../objects/locations";
import { title } from "../fields/text";
import { benefitId } from "./benefit";

// maximum number of locations to display in the preview without truncating
const LOCATIONS_PREVIEW_CUTOFF = 3;
import { bonusesByLocation } from "../objects/compensations/bonusesByLocation";

export const compensationsId = "compensations";

Expand All @@ -15,74 +12,59 @@ const compensations = defineType({
type: "document",
title: "Compensations",
fields: [
locations,
title,
{
...title,
title: "Compensation Page Title",
description:
"Enter the primary title that will be displayed at the top of the compensation page. This is what users will see when they visit the page.",
},
titleSlug,
seo,
defineField({
name: "showSalaryCalculator",
title: "Show Salary Calculator",
description: "Should the salary calculator be visible on the page?",
type: "boolean",
initialValue: true,
}),
// add pension here. pension doesn't rely on locations
bonusesByLocation,
// add salary here
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to keep these comments so we know what's expected here once we add pension, benefits and salary

// benefits should be updated to match the grouping by location:
// defineField({
// name: "benefits",
// title: "Benefits",
// type: "array",
// of: [
// defineField({
// name: "benefitGroup",
// title: "Benefit Group",
// type: "object",
// fields: [
// locations,
// defineField({
// name: "benefitsList",
// title: "List of Benefits",
// type: "array",
// of: [{ type: benefitId }],
// }),
// ],
// }),
// ],
// }),
// IMPORTANT: this is just a very simple mockup and might not represent a good ux
defineField({
christinaroise marked this conversation as resolved.
Show resolved Hide resolved
name: "benefits",
title: "Benefits",
description: "Manage benefits for the compensations page",
title: "Included Benefits",
description:
"Add and manage information on the benefits included with the compensation package, such as health insurance, retirement plans, and paid time off.",
type: "array",
of: [{ type: benefitId }],
}),
seo,
],
preview: {
select: {
title: "basicTitle",
/*
Access array object values using dot notation with the array index, e.g., "locations.0.basicTitle".
This approach allows selecting a subset of the array for preview purposes.
For more details, see: https://www.sanity.io/docs/previews-list-views#62febb15a63a
*/
...[...Array(LOCATIONS_PREVIEW_CUTOFF + 1).keys()].reduce(
(o, i) => ({ ...o, [`location${i}`]: `locations.${i}.basicTitle` }),
{},
),
title: "title",
},
prepare({ title, ...locationsMap }) {
prepare({ title }) {
return {
title,
subtitle: previewStringFromLocationsMap(
locationsMap,
LOCATIONS_PREVIEW_CUTOFF,
),
};
},
},
});

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we most likely want to copy paste this into benefits and salary but removing for now.

* Generates a preview string based on the selected office locations.
*
* @param {Object} locationsMap - A map of office titles selected for preview, e.g., { office0: "Trondheim", office1: "Oslo", ... }.
* @param {number} cutoff - The maximum number of locations to display before shortening the preview string.
* @returns {string|undefined} - A formatted string summarizing the selected locations or `undefined` if no locations are selected.
*/
function previewStringFromLocationsMap(
locationsMap: {
[key: string]: string;
},
cutoff: number,
): string | undefined {
const locations = Object.values<string>(locationsMap).filter(
(o) => o !== undefined,
);
if (locations.length === 0) {
return undefined;
} else if (locations.length === 1) {
return `Location: ${locations[0]}`;
} else if (locations.length > cutoff) {
return `Locations: ${locations.toSpliced(cutoff - 1).join(", ")}, and more`;
}
return `Locations: ${locations.toSpliced(-1).join(", ")} and ${locations.at(-1)}`;
}

export default compensations;
14 changes: 0 additions & 14 deletions studio/schemas/documents/location.ts

This file was deleted.

97 changes: 97 additions & 0 deletions studio/schemas/objects/compensations/bonusesByLocation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { defineField } from "sanity";
import { location, locationID } from "../locations";
import { companyLocationID } from "studio/schemas/documents/companyLocations";

export const bonusesByLocation = defineField({
name: "bonusesByLocation",
title: "Bonus by Location",
description:
"Enter the average bonus amount for each company location. Each location can have only one bonus entry, but you can assign the same bonus amount to multiple locations.",
type: "array",
of: [
defineField({
name: "bonusData",
title: "Bonus Information",
description:
"Details of the bonus amount specific to a particular location. Each location should have a unique entry with the average bonus calculated for that site.",
type: "object",
fields: [
{
...location,
description:
"Select the company location for which you are entering the average bonus information. Each location must be unique.",
validation: (Rule) => Rule.required(),
},
defineField({
name: "averageBonus",
title: "Average Bonus",
description:
"Enter the average bonus amount for this location. Ensure the amount is positive and reflective of the compensation package for this location.",
type: "number",
validation: (Rule) =>
Rule.required()
.min(0)
.error("Please enter a positive bonus amount."),
}),
],
preview: {
select: {
averageBonus: "averageBonus",
location: `${locationID}.${companyLocationID}`,
},
prepare({ averageBonus, location }) {
return {
title: `Average Bonus: ${averageBonus || "N/A"}`,
subtitle: `Location: ${location || "No location selected"}`,
};
},
},
}),
],
validation: (Rule) =>
Rule.custom((bonusesByLocation, context) => {
const duplicateCheck = checkForDuplicateLocations(
bonusesByLocation as BonusEntry[] | undefined,
);

if (duplicateCheck !== true) return duplicateCheck;

return true;
christinaroise marked this conversation as resolved.
Show resolved Hide resolved
}),
});

interface LocationReference {
_ref: string;
_type: string;
title?: string;
}

interface BonusEntry {
location: LocationReference;
averageBonus: number;
}

/**
* Checks for duplicate location references in the bonusesByLocation array.
* Ensures each location has a unique bonus entry.
*
* @param {BonusEntry[] | undefined} bonusesByLocation - The array of bonus entries, each with one or more locations.
* @returns {string | true} - Returns an error message if duplicate locations are found, or true if all are unique.
*/
const checkForDuplicateLocations = (
bonusesByLocation: BonusEntry[] | undefined,
): string | true => {
if (!bonusesByLocation) return true;

const locationRefs = bonusesByLocation
.map((entry) => entry.location?._ref)
.filter(Boolean);

const uniqueRefs = new Set(locationRefs);

if (uniqueRefs.size !== locationRefs.length) {
return "Each location should be listed only once in the bonuses list. You can assign the same bonus amount to multiple locations, but make sure no location appears more than once.";
}

return true;
};
34 changes: 19 additions & 15 deletions studio/schemas/objects/locations.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { defineField } from "sanity";
import { locationId } from "../documents/location";
import { companyLocationsID } from "../documents/companyLocations";

const locations = defineField({
name: "locations",
export const locationsID = "locations";
export const locationID = "location";

export const location = defineField({
name: locationID,
type: "reference",
title: "Select a location",
description:
"Select the office location this content applies to. If it applies to all locations, you can leave this field empty.",
to: [{ type: companyLocationsID }],
options: {
disableNew: true,
},
});

export const locations = defineField({
name: locationsID,
type: "array",
title: "Relevant Locations",
description:
"You can tailor this content to specific office locations by selecting them here. If the content applies to all locations, just leave this field empty.",
of: [
{
title: "Select a location",
type: "reference",
to: [{ type: locationId }],
options: {
disableNew: true,
},
},
],
of: [location],
});

export default locations;