Skip to content

Accommodation revamp #352

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

Merged
merged 8 commits into from
Jun 10, 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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "traitify-widgets",
"version": "3.6.0",
"version": "3.7.0",
"description": "Traitiy Widgets",
"repository": {
"type": "git",
Expand Down
22 changes: 11 additions & 11 deletions public/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ function createWidget() {
Traitify.options.locale = cache.get("locale");
Traitify.options.perspective = cache.get("perspective");
Traitify.options.report = cache.get("report");
["showHeaders", "showInstructions"].forEach((key) => {
["showHeaders"].forEach((key) => {
const value = booleanFrom(cache.get(key), "default");

if(value !== "default") { Traitify.options[key] = value; }
});
Traitify.options.showRecommendationList = true;
Traitify.options.showTraitList = true;
Traitify.options.survey = {};
["allowBack", "allowFullscreen"].forEach((key) => {
["allowBack", "allowFullscreen", "showInstructions"].forEach((key) => {
const value = booleanFrom(cache.get(`survey.${key}`), "default");

if(value !== "default") { Traitify.options.survey[key] = value; }
Expand Down Expand Up @@ -316,15 +316,6 @@ function setupDom() {
],
text: "Show Headers:"
}));
column.appendChild(createOption({
name: "showInstructions",
options: [
{text: "Default", value: ""},
{text: "Yes", value: "true"},
{text: "No", value: "false"}
],
text: "Show Instructions:"
}));
row.appendChild(column);
column = createElement();
column.appendChild(createElement({className: "column-header", text: "Survey Options"}));
Expand All @@ -346,6 +337,15 @@ function setupDom() {
],
text: "Allow Fullscreen:"
}));
column.appendChild(createOption({
name: "survey.showInstructions",
options: [
{text: "Default", value: ""},
{text: "Yes", value: "true"},
{text: "No", value: "false"}
],
text: "Show Instructions:"
}));
row.appendChild(column);
group.appendChild(row);
row = createElement({className: "row"});
Expand Down
2 changes: 1 addition & 1 deletion src/components/container/hooks/use-order-polling.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useOrder from "lib/hooks/use-order";
import {orderIDState, orderState} from "lib/recoil";

const defaultPollingTimes = {
long: {interval: 10 * 1000, stop: 5 * 60 * 1000},
long: {interval: 10 * 1000, stop: 10 * 24 * 60 * 60 * 1000},
none: {interval: null, stop: null},
short: {interval: 5 * 1000, stop: 1 * 60 * 1000}
};
Expand Down
5 changes: 4 additions & 1 deletion src/components/container/hooks/use-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export default function useProps(props) {
} = props;

useEffect(() => {
setOptions({showHeaders: false, showInstructions: true, ...options});
// NOTE: showInstructions should be true but we don't want a breaking change
const surveyOptions = {showInstructions: false, ...(options.survey || {})};

setOptions({showHeaders: false, ...options, survey: surveyOptions});
}, [options]);

useEffect(() => {
Expand Down
3 changes: 3 additions & 0 deletions src/components/default.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import useActive from "lib/hooks/use-active";
import useOrder from "lib/hooks/use-order";
import Results from "./results";
import Status from "./status";
import Survey from "./survey";

export default function Default() {
const active = useActive();
const order = useOrder();

if(order?.status === "skipped") { return <Status />; }
if(!active) { return <Status />; }
if(active.loading) { return <Status />; }
if(active.surveyType === "external") { return <Status />; }
Expand Down
2 changes: 2 additions & 0 deletions src/components/results/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import EmployeeReport from "components/report/employee";
import ManagerReport from "components/report/manager";
import Cognitive from "components/results/cognitive";
import FinancialRiskResults from "components/results/financial-risk";
import Skipped from "components/status/skipped";
import useActive from "lib/hooks/use-active";
import useComponentEvents from "lib/hooks/use-component-events";
import useOption from "lib/hooks/use-option";
Expand All @@ -17,6 +18,7 @@ export default function Results() {
useComponentEvents("Results");

if(!active) { return null; }
if(active.skipped) { return <Skipped />; }
if(!active.completed) { return null; }
if(active.surveyType === "cognitive") { return <Cognitive />; }
if(active.surveyType !== "personality") { return null; }
Expand Down
35 changes: 15 additions & 20 deletions src/components/status/assessment.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,53 @@ import Icon from "components/common/icon";
import get from "lib/common/object/get";
import useListener from "lib/hooks/use-listener";
import useOption from "lib/hooks/use-option";
import useTranslate from "lib/hooks/use-translate";
import {activeState} from "lib/recoil";
import style from "./style.scss";

// TODO: Extract text to translate
const translations = {
complete: "Complete",
loading: "Loading", // Already translated
status: {
start: "Start Assessment"
},
survey: {
cognitive_assessment: "Cognitive Assessment",
external_assessment: "External Assessment",
personality_assessment: "Personality Assessment"
}
};

function Button({assessment}) {
const listener = useListener();
const options = useOption("status") || {};
const redirect = get(options, "allowRedirect", true);
const setActive = useSetRecoilState(activeState);
const translate = useTranslate();

if(assessment.completed) {
return <button disabled={true} type="button">{translations.complete}</button>;
return <button disabled={true} type="button">{translate("complete")}</button>;
}

if(assessment.skipped) {
return <button disabled={true} type="button">{translate("skipped")}</button>;
}

if(assessment.link && redirect) {
return <a href={assessment.link}>{translations.status.start}</a>;
return <a href={assessment.link}>{translate("status.start")}</a>;
}

if(assessment.loading) {
return <button disabled={true} type="button">{translations.loading}</button>;
return <button disabled={true} type="button">{translate("loading")}</button>;
}

const start = () => {
listener.trigger("Survey.start", {assessment});
setActive({...assessment});
};

return <button onClick={start} type="button">{translations.status.start}</button>;
return <button onClick={start} type="button">{translate("status.start")}</button>;
}

Button.propTypes = {
assessment: PropTypes.shape({
completed: PropTypes.bool,
link: PropTypes.string,
loading: PropTypes.bool
loading: PropTypes.bool,
skipped: PropTypes.bool
}).isRequired
};

function Assessment({assessment}) {
const surveyName = assessment.surveyName || translations.survey[`${assessment.surveyType}_assessment`];
const translate = useTranslate();
const surveyName = assessment.surveyName || translate(`survey.${assessment.surveyType}_assessment`);

return (
<div className={[style.assessment, assessment.completed && style.inactive].filter(Boolean).join(" ")}>
Expand All @@ -73,6 +67,7 @@ function Assessment({assessment}) {
Assessment.propTypes = {
assessment: PropTypes.shape({
completed: PropTypes.bool,
skipped: PropTypes.bool,
surveyName: PropTypes.string,
surveyType: PropTypes.string.isRequired
}).isRequired
Expand Down
18 changes: 18 additions & 0 deletions src/components/status/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {useRecoilRefresher_UNSTABLE as useRecoilRefresher} from "recoil";
import useTranslate from "lib/hooks/use-translate";
import {orderState} from "lib/recoil";
import style from "./style.scss";

export default function Error() {
const refreshOrder = useRecoilRefresher(orderState);
const translate = useTranslate();
const retry = () => { refreshOrder(); };

return (
<div className={style.container}>
<div className={style.header}>{translate("status.heading")}</div>
<div className={style.p}>{translate("status.error")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("status.try_again")}</button>
</div>
);
}
69 changes: 13 additions & 56 deletions src/components/status/index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
import {useEffect, useState} from "react";
import {useRecoilRefresher_UNSTABLE as useRecoilRefresher} from "recoil";
import useActive from "lib/hooks/use-active";
import useComponentEvents from "lib/hooks/use-component-events";
import useListener from "lib/hooks/use-listener";
import useOrder from "lib/hooks/use-order";
import {orderState} from "lib/recoil";
import useTranslate from "lib/hooks/use-translate";
import Assessment from "./assessment";
import Error from "./error";
import Loading from "./loading";
import Skipped from "./skipped";
import style from "./style.scss";

// TODO: Translate probably
const translations = {
status: {
error: "There was an error with your assessments",
header: "Your Application Assessments",
loading: "Loading your assessment",
text: "As part of your application, we'd like to ask you to complete the following assessments. Please click on the button next to the assessment name. This will take you to where you'll complete the assessment and either be returned to this page if you have multiple assessments to complete or taken to the next stage of the process.",
timeout: "Loading timed out",
try_again: "Let's Try Again"
}
};
import Timeout from "./timeout";

export default function Status() {
const active = useActive();
const listener = useListener();
const order = useOrder();
const [activeLoading, setActiveLoading] = useState(false);
const refreshOrder = useRecoilRefresher(orderState);
const order = useOrder();
const translate = useTranslate();
const assessments = order?.assessments || [];

useComponentEvents("Status", {order});
Expand All @@ -40,48 +29,16 @@ export default function Status() {
}, [active?.id, active?.completed]);

if(!order) { return null; }
if(order.status === "error") {
const retry = () => { refreshOrder(); };

return (
<div className={style.container}>
<div className={style.header}>{translations.status.header}</div>
<div className={style.p}>{translations.status.error}</div>
{assessments.map((assessment) => (
<Assessment key={assessment.id} assessment={assessment} />
))}
<button className={style.retry} onClick={retry} type="button">{translations.status.try_again}</button>
</div>
);
}

if(activeLoading || active?.loading || order.status === "loading") {
return (
<div className={style.container}>
<img alt="" src="https://cdn.traitify.com/widgets/status/loading-sonar.gif" />
<div className={[style.bold, style.p].join(" ")}>{translations.status.loading}</div>
</div>
);
}

if(order.status === "timeout") {
const retry = () => { listener.trigger("Order.polling", {status: "on"}); };

return (
<div className={style.container}>
<div className={style.spacer} />
<div className={[style.bold, style.p].join(" ")}>{translations.status.timeout}</div>
<button className={style.retry} onClick={retry} type="button">{translations.status.try_again}</button>
</div>
);
}

if(order.status === "error") { return <Error />; }
if(order.status === "skipped") { return <Skipped />; }
if(activeLoading || active?.loading || order.status === "loading") { return <Loading />; }
if(order.status === "timeout") { return <Timeout />; }
if(assessments.length === 0) { return null; }

return (
<div className={style.container}>
<div className={style.header}>{translations.status.header}</div>
<div className={style.p}>{translations.status.text}</div>
<div className={style.header}>{translate("status.heading")}</div>
<div className={style.p}>{translate("status.text")}</div>
{assessments.map((assessment) => <Assessment key={assessment.id} assessment={assessment} />)}
</div>
);
Expand Down
13 changes: 13 additions & 0 deletions src/components/status/loading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import useTranslate from "lib/hooks/use-translate";
import style from "./style.scss";

export default function Loading() {
const translate = useTranslate();

return (
<div className={style.container}>
<img alt="" src="https://cdn.traitify.com/widgets/status/loading-sonar.gif" />
<div className={[style.bold, style.p].join(" ")}>{translate("status.loading")}</div>
</div>
);
}
14 changes: 14 additions & 0 deletions src/components/status/skipped.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import DangerousHTML from "components/common/dangerous-html";
import useTranslate from "lib/hooks/use-translate";
import style from "./style.scss";

export default function Skipped() {
const translate = useTranslate();

return (
<section className={style.container}>
<div className={style.header}>{translate("survey.accommodation.submitted")}</div>
<DangerousHTML className={style.p} html={translate("survey.accommodation.submitted_text")} />
</section>
);
}
17 changes: 17 additions & 0 deletions src/components/status/timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import useListener from "lib/hooks/use-listener";
import useTranslate from "lib/hooks/use-translate";
import style from "./style.scss";

export default function Timeout() {
const listener = useListener();
const translate = useTranslate();
const retry = () => { listener.trigger("Order.polling", {status: "on"}); };

return (
<div className={style.container}>
<div className={style.spacer} />
<div className={[style.bold, style.p].join(" ")}>{translate("status.timeout")}</div>
<button className={style.retry} onClick={retry} type="button">{translate("status.try_again")}</button>
</div>
);
}
Loading