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

[Feat] Salary ranges dialog #12412

Merged
merged 4 commits into from
Jan 3, 2025
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
40 changes: 36 additions & 4 deletions apps/web/src/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,10 @@
"defaultMessage": "Note spΓ©ciale (anglais)",
"description": "Label for a process' English special note"
},
"11/un8": {
"defaultMessage": "nous <strong>recherchons un employΓ© hautement qualifiΓ© ou expΓ©rimentΓ©</strong> qui peut assumer immΓ©diatement toutes les responsabilitΓ©s du poste.",
"description": "List of conditions for starting at a higher rate, item 3"
},
"13fSK+": {
"defaultMessage": "La date de soumission est dΓ©passΓ©e",
"description": "Status for an application that where the recruitment has expired"
Expand Down Expand Up @@ -647,6 +651,10 @@
"defaultMessage": "Aucun courriel fourni",
"description": "Fallback for email value"
},
"1JtwAU": {
"defaultMessage": "La plupart des nouveaux membres du personnel commencent au premier Γ©chelon de l’échelle applicable. <strong>Pour commencer Γ  un Γ©chelon plus Γ©levΓ©</strong>, le ou la gestionnaire d’embauche doit justifier pourquoi un salaire plus Γ©levΓ© que le premier Γ©chelon est jugΓ© nΓ©cessaire pour recruter une personne ayant les compΓ©tences requises. Il ou elle devra expliquer comment le poste rΓ©pond Γ  l’une des trois conditions suivantesΒ :Β ",
"description": "Second paragraph for the pool application salary ranges dialog"
},
"1MJGsD": {
"defaultMessage": "Cette possibilité d'emploi s'adresse aux employés internes de classification {classification} ou équivalente avec la préférence accordée à ceux aux ministères énumérés*",
"description": "Title of a note describing that a pool is only open to employees, at-level, with departmental preference. Has an asterisk for fine print."
Expand Down Expand Up @@ -1415,6 +1423,10 @@
"defaultMessage": "Ensemble, nous sommes en mesure de tirer parti de la diversitΓ© des expΓ©riences et des idΓ©es que les personnes autochtones apportent Γ  la fonction publique et de contribuer Γ  la rΓ©conciliation au Canada.",
"description": "Hero subtitle for IAP manager homepage"
},
"57DYUv": {
"defaultMessage": "nous rencontrons des <strong>difficultΓ©s exceptionnelles Γ  pourvoir le poste</strong> avec des candidats qualifiΓ©s, possiblement parce que le premier Γ©chelon de l’échelle salariale n’est pas concurrentiel;",
"description": "List of conditions for starting at a higher rate, item 2"
},
"58+Hom": {
"defaultMessage": "Modèles d'offres d'emploi",
"description": "Heading for the page showing list of job poster templates"
Expand Down Expand Up @@ -2351,6 +2363,10 @@
"defaultMessage": "Mettre Γ  jour l’expΓ©rience",
"description": "Button to submit the link experience to skill form"
},
"AAisdi": {
"defaultMessage": "En savoir plus sur les Γ©chelles salariales",
"description": "Info button label for pool application salary range details."
},
"AAvLM5": {
"defaultMessage": "Type d’études",
"description": "Label displayed on Education form for education type input"
Expand Down Expand Up @@ -3991,10 +4007,6 @@
"defaultMessage": "Erreur : impossible d’annuler le statut de la dΓ©cision finale",
"description": "Message displayed when an error reverting final decision status of pool candidate"
},
"IvJ9Xd": {
"defaultMessage": "Information sur l'Γ©chelle salariale",
"description": "Link text to more information about classification salary range"
},
"Ivvi/F": {
"defaultMessage": "Avez-vous utilisΓ© certaines techniques ou approches spΓ©ciales?",
"description": "Question about techniques used for a skill"
Expand Down Expand Up @@ -4319,6 +4331,10 @@
"defaultMessage": "AC",
"description": "Short form code representing 'veteran'"
},
"KYXYsD": {
"defaultMessage": "Les Γ©chelles salariales de tous les postes de la fonction publique fΓ©dΓ©rale sont <link>publiquement accessibles</link>. Les annonces d’emploi indiquent l’échelle complΓ¨te pour un poste donnΓ©.",
"description": "First paragraph for the pool application salary ranges dialog"
},
"KZtBcM": {
"defaultMessage": "Examiner la demande<hidden> {name}</hidden>",
"description": "Link text to view a specific application"
Expand Down Expand Up @@ -5299,6 +5315,10 @@
"defaultMessage": "CrΓ©er<hidden> un regroupement de compΓ©tences</hidden>",
"description": "Breadcrumb title for the create skill family page link."
},
"PROKKo": {
"defaultMessage": "Vous trouverez les Γ©chelles salariales et les Γ©chelons de la plupart des emplois du gouvernement du Canada dans <link1>les conventions collectives</link1>. Pour les postes non syndiquΓ©s, les Γ©chelles salariales sont disponibles sur le <link2>site Web du SecrΓ©tariat du Conseil du TrΓ©sor du Canada</link2>.",
"description": "Fourth paragraph for the pool application salary ranges dialog"
},
"PS/LFb": {
"defaultMessage": "<strong>Non</strong>, je ne suis pas un(e) employΓ©(e) du gouvernement du Canada.",
"description": "Label displayed for is not a government employee option"
Expand Down Expand Up @@ -6135,6 +6155,10 @@
"defaultMessage": "CollectivitΓ© {communityId} introuvable.",
"description": "Message displayed for community not found."
},
"TgjkyB": {
"defaultMessage": "AprΓ¨s votre embauche, vous monterez d’un Γ©chelon dans l’échelle salariale chaque annΓ©e, Γ  la date anniversaire de votre entrΓ©e en fonction. Vous changerez d’échelon jusqu’à ce que vous atteigniez l’échelon maximal. Une fois que vous aurez atteint l’échelon le plus Γ©levΓ©, votre salaire ne changera que si votre convention collective change ou si vous acceptez un nouveau poste Γ  un niveau supΓ©rieur.",
"description": "Third paragraph for the pool application salary ranges dialog"
},
"TiIkSF": {
"defaultMessage": "Γ‰tudes postsecondaires de deux ans",
"description": "Option for education requirement, 2-year post-secondary"
Expand Down Expand Up @@ -10870,6 +10894,10 @@
"defaultMessage": "Cette compétence possède les expériences connexes suivantes :",
"description": "An introduction to a list of experiences associated with a skill"
},
"rdxU+x": {
"defaultMessage": "Γ‰chelle salariale",
"description": "Heading for the salary ranges dialog"
},
"rfDHc0": {
"defaultMessage": "(Aucun changement)",
"description": "Null state, nothing changed yet."
Expand Down Expand Up @@ -12134,6 +12162,10 @@
"defaultMessage": "contiennent des renseignements personnels",
"description": "Comments or contributions list item"
},
"zA8GrW": {
"defaultMessage": "il y a une <strong>pΓ©nurie de travailleurs qualifiΓ©s</strong> dans ce domaine, selon des enquΓͺtes locales ou rΓ©gionales du marchΓ© du travail menΓ©es par des organisations reconnues;",
"description": "List of conditions for starting at a higher rate, item 1"
},
"zACLaV": {
"defaultMessage": "Position dans le groupe requis non autorisΓ©e en raison d’une restriction de classification",
"description": "Unable to create new positions in classification operations consideration"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import {

import {
formatClassificationString,
getClassificationSalaryRangeUrl,
getFullPoolTitleHtml,
getShortPoolTitleLabel,
isAdvertisementVisible,
Expand Down Expand Up @@ -76,6 +75,7 @@ import ClosedEarlyDeadlineDialog from "./components/ClosedEarlyDeadlineDialog";
import DeadlineValue from "./components/DeadlineValue";
import AreaOfSelectionWell from "./components/AreaOfSelectionWell";
import WhoCanApplyText from "./components/WhoCanApplyText";
import SalaryRangeDialog from "./components/SalaryRangeDialog";

interface SectionContent {
id: string;
Expand Down Expand Up @@ -340,10 +340,6 @@ export const PoolPoster = ({
classification: pool.classification,
});
const formattedSubTitle = intl.formatMessage(subTitle);
const salaryRangeUrl = getClassificationSalaryRangeUrl(
locale,
classification,
);
const workLocation = pool.isRemote
? intl.formatMessage({
defaultMessage: "Remote, hybrid or on-site",
Expand Down Expand Up @@ -710,23 +706,7 @@ export const PoolPoster = ({
locale,
) ?? notAvailable
}
suffix={
salaryRangeUrl && (
<Link
mode="icon_only"
external
newTab
href={salaryRangeUrl}
icon={InformationCircleIcon}
aria-label={`${intl.formatMessage({
defaultMessage: "Salary range information",
id: "IvJ9Xd",
description:
"Link text to more information about classification salary range",
})} ${intl.formatMessage(uiMessages.newTab)}`}
/>
)
}
suffix={<SalaryRangeDialog />}
/>
<DataRow
label={
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useIntl } from "react-intl";
import InformationCircleIcon from "@heroicons/react/24/solid/InformationCircleIcon";
import { ReactNode } from "react";

import { Button, Dialog, Link, LinkProps } from "@gc-digital-talent/ui";
import { getLocale } from "@gc-digital-talent/i18n";

const generateLink = (href: LinkProps["href"], chunks: ReactNode) => (
<Link newTab external href={href}>
{chunks}
</Link>
);

const rateOfPayUrl = {
en: "https://www.tbs-sct.canada.ca/pubs_pol/hrpubs/coll_agre/rates-taux-eng.asp",
fr: "https://www.tbs-sct.canada.ca/pubs_pol/hrpubs/coll_agre/rates-taux-fra.asp",
} as const;

const rateOfPayNonUnionUrl = {
en: "https://www.canada.ca/en/treasury-board-secretariat/services/pay/rates-pay/rates-pay-unrepresented-senior-excluded-employees.html",
fr: "https://www.canada.ca/fr/secretariat-conseil-tresor/services/remuneration/taux-remuneration/taux-remuneration-employes-non-representes-exclus-niveaux-superieurs.html",
} as const;

const DeadlineDialog = () => {
const intl = useIntl();
const locale = getLocale(intl);

return (
<Dialog.Root>
<Dialog.Trigger>
<Button
mode="icon_only"
color="secondary"
icon={InformationCircleIcon}
aria-label={intl.formatMessage({
defaultMessage: "Learn more about salary ranges",
id: "AAisdi",
description:
"Info button label for pool application salary range details.",
})}
/>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
{intl.formatMessage({
defaultMessage: "Salary range",
id: "rdxU+x",
description: "Heading for the salary ranges dialog",
})}
</Dialog.Header>
<Dialog.Body>
<div
data-h2-display="base(flex)"
data-h2-gap="base(x.5)"
data-h2-flex-direction="base(column)"
data-h2-align-items="base(flex-start)"
>
<p>
{intl.formatMessage(
{
defaultMessage:
"The salary ranges for all federal public service positions are <link>shared publicly</link>. The job advertisement shows the full range for this type of position.",
id: "KYXYsD",
description:
"First paragraph for the pool application salary ranges dialog",
},
{
link: (chunks: ReactNode) =>
generateLink(rateOfPayUrl[locale], chunks),
},
)}
</p>
<p>
{intl.formatMessage({
defaultMessage:
"Most new hires will start at the lowest salary in the applicable range. <strong>To start at a higher rate</strong>, the hiring manager will need to justify why a salary above the minimum is considered necessary to obtain a suitably qualified person. They’ll need to explain how the position meets 1 of 3 conditions:",
id: "1JtwAU",
description:
"Second paragraph for the pool application salary ranges dialog",
})}
</p>
<ol data-h2-margin-bottom="base:children[:not(:last-child)](x0.5)">
<li>
{intl.formatMessage({
defaultMessage:
"There is a <strong>shortage of skilled workers</strong> in the field, based on local or regional labour market surveys from recognized institutions.",
id: "zA8GrW",
description:
"List of conditions for starting at a higher rate, item 1",
})}
</li>
<li>
{intl.formatMessage({
defaultMessage:
"There are <strong>unusual difficulties in filling the role</strong> with qualified candidates, possibly because the minimum rate in the salary range isn’t competitive.",
id: "57DYUv",
description:
"List of conditions for starting at a higher rate, item 2",
})}
</li>
<li>
{intl.formatMessage({
defaultMessage:
"There is a <strong>need for a highly skilled or experienced employee</strong> who can immediately take on the full responsibilities of the position.",
id: "11/un8",
description:
"List of conditions for starting at a higher rate, item 3",
})}
</li>
</ol>
<p>
{intl.formatMessage({
defaultMessage:
"Once you’re hired, you’ll move up a step in the salary range each year on the anniversary of your start date. This will continue until you reach the maximum rate. Once you’re at the highest step, your salary will change only if your collective agreement changes or if you take a new job at a higher level.",
id: "TgjkyB",
description:
"Third paragraph for the pool application salary ranges dialog",
})}
</p>
<p>
{intl.formatMessage(
{
defaultMessage:
"You can find the salary ranges and steps for most Government of Canada jobs in <link1>the collective agreements</link1>. For non-unionized positions, the salary range is available on the <link2>Treasury Board of Canada Secretariat’s website</link2>.",
id: "PROKKo",
description:
"Fourth paragraph for the pool application salary ranges dialog",
},
{
link1: (chunks: ReactNode) =>
generateLink(rateOfPayUrl[locale], chunks),
link2: (chunks: ReactNode) =>
generateLink(rateOfPayNonUnionUrl[locale], chunks),
},
)}
</p>
</div>
<Dialog.Footer>
<Dialog.Close>
<Button color="secondary">
{intl.formatMessage({
defaultMessage: "Close",
id: "4p0QdF",
description: "Button text used to close an open modal",
})}
</Button>
</Dialog.Close>
</Dialog.Footer>
</Dialog.Body>
</Dialog.Content>
</Dialog.Root>
);
};

export default DeadlineDialog;
48 changes: 0 additions & 48 deletions apps/web/src/utils/poolUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import LockClosedIcon from "@heroicons/react/20/solid/LockClosedIcon";
import { ReactNode } from "react";

import {
Locales,
commonMessages,
getLocalizedName,
navigationMessages,
Expand Down Expand Up @@ -466,50 +465,3 @@ export function getClassificationName(
const nameStr = getLocalizedName(name, intl);
return `${groupLevelStr} (${nameStr})`;
}

export const getClassificationSalaryRangeUrl = (
locale: Locales,
classification?: Maybe<Pick<Classification, "group">>,
): string | null => {
let localizedUrl: Record<Locales, string> | null = null;
switch (classification?.group) {
case "CS":
case "IT":
localizedUrl = {
en: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-eng.aspx?id=1",
fr: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-fra.aspx?id=1",
};
break;
case "AS":
case "PM":
localizedUrl = {
en: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-eng.aspx?id=15",
fr: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-fra.aspx?id=15",
};
break;
case "EC":
localizedUrl = {
en: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-eng.aspx?id=4",
fr: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-fra.aspx?id=4",
};
break;
case "CR":
localizedUrl = {
en: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-eng.aspx?id=15",
fr: "https://www.tbs-sct.canada.ca/agreements-conventions/view-visualiser-fra.aspx?id=15",
};
break;
case "EX":
localizedUrl = {
en: "https://www.canada.ca/en/treasury-board-secretariat/services/pay/rates-pay/rates-pay-unrepresented-senior-excluded-employees/ex.html#EXcurrent",
fr: "https://www.canada.ca/fr/secretariat-conseil-tresor/services/remuneration/taux-remuneration/taux-remuneration-employes-non-representes-exclus-niveaux-superieurs/ex.htm#EXcurrent",
};
break;
default:
break;
}

if (localizedUrl) return localizedUrl[locale];

return null;
};
Loading