Skip to content

Commit

Permalink
cleanup compensation and group location specific bonus
Browse files Browse the repository at this point in the history
  • Loading branch information
christinaroise committed Sep 4, 2024
1 parent f56a048 commit b9dc346
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 131 deletions.
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";
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,
],
};
24 changes: 16 additions & 8 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 @@ -36,13 +37,17 @@ export default (S: StructureBuilder) =>
S.document()
.schemaType(companyInfoID)
.documentId(companyInfoID)
.title("Company Information"),
.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()
.title("Legal Documents")
Expand All @@ -59,7 +64,7 @@ export default (S: StructureBuilder) =>
S.document()
.schemaType("navigationManager")
.documentId("navigationManager")
.title("Navigation Manager"),
.title("Navigation Manager")
),
S.listItem()
.title("Dynamic Pages")
Expand All @@ -79,14 +84,17 @@ export default (S: StructureBuilder) =>
S.document()
.schemaType(blogId)
.documentId(blogId)
.title("Blog Overview & Settings"),
.title("Blog Overview & Settings")
),
S.listItem()
.title("Compensations")
.icon(HeartIcon)
.child(
S.documentTypeList(compensationsId).title("Compensations"),
S.document()
.schemaType(compensationsId)
.documentId(compensationsId)
.title("Compensations")
),
]),
])
),
]);
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;
84 changes: 35 additions & 49 deletions studio/schemas/documents/compensations.ts
Original file line number Diff line number Diff line change
@@ -1,13 +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";
import { compensationDetails } from "../objects/compensationData";

// 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 @@ -16,10 +12,39 @@ const compensations = defineType({
type: "document",
title: "Compensations",
fields: [
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,
locations,
compensationDetails,
// add pension here. pension doesn't rely on locations
bonusesByLocation,
// add salary here
// 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({
name: "benefits",
title: "Included Benefits",
Expand All @@ -32,53 +57,14 @@ const compensations = defineType({
],
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,
),
};
},
},
});

/**
* 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.

41 changes: 0 additions & 41 deletions studio/schemas/objects/compensationData.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;
}),
});

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;
};
Loading

0 comments on commit b9dc346

Please sign in to comment.