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

Validate company application page #309

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
280f5bf
Added validation of company application route and unverified state
FranciscoCardoso913 Feb 4, 2023
6284d08
Implemented validation page design
FranciscoCardoso913 Feb 6, 2023
aa0ac75
Added appropriate error messages for application
FranciscoCardoso913 Feb 11, 2023
a246730
Changed hyperlinks and redirect to Link component and changed text to…
FranciscoCardoso913 May 9, 2023
80cfaf3
Solved redirected to home page issue and refactored validation page code
FranciscoCardoso913 Jun 20, 2023
b328a12
Added a waring to MyOffers page and to the CreateOffer page when comp…
FranciscoCardoso913 Jun 20, 2023
502ae8f
Added a waring to MyOffers page and to the CreateOffer page when comp…
FranciscoCardoso913 Jun 20, 2023
61dafc3
Merge remote-tracking branch 'origin/validate-company-application-Pag…
FranciscoCardoso913 Jun 20, 2023
df27afe
Conditionally render the alert for non yet approved companies and cha…
dsantosferreira Jun 27, 2023
02072ab
Rewriting the website messages to match the current flow
FranciscoCardoso913 Jul 4, 2023
52dbd3f
Rewriting the website messages to match the current flow
FranciscoCardoso913 Jul 4, 2023
a327f00
Fixing failing tests
FranciscoCardoso913 Aug 8, 2023
4c33b8d
Created test to check if approve and reject buttons are not rendered …
dsantosferreira Aug 18, 2023
3a5a721
Tests for validation page
FranciscoCardoso913 Aug 19, 2023
4f85e5f
Tests for validation page
FranciscoCardoso913 Aug 19, 2023
95794d0
Tests for the alerts components in the company offer management page …
FranciscoCardoso913 Aug 19, 2023
4198681
Making tests for the validate application and the fetch company appli…
FranciscoCardoso913 Aug 29, 2023
decd155
Add extra error message to validation page, fixed: can enter create o…
FranciscoCardoso913 Sep 16, 2023
23032fa
Merge branch 'develop' into validate-company-application-Page
FranciscoCardoso913 Sep 16, 2023
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
10 changes: 10 additions & 0 deletions src/AppRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import EditOfferPage from "./pages/EditOfferPage";
import PrivacyPolicyPage from "./pages/PrivacyPolicyPage";
import TermsAndConditionsPage from "./pages/TermsAndConditionsPage";
import ChangeLogPage from "./pages/ChangeLogPage";
import ValidationPage from "./pages/ValidationPage";

/**
*
Expand Down Expand Up @@ -135,6 +136,15 @@ const AppRouter = () => (
<RulesPage />
</PageLayout>
</Route>
<Route exact path="/apply/:token/validate" key="/apply/:token/validate">
<PageLayout
key="/apply/:token/validate"
pageTitle="Validate Company Application"
layout={LayoutType.DESKTOP}
>
<ValidationPage />
</PageLayout>
</Route>
<ProtectedRoute
exact
key="/review/applications"
Expand Down
8 changes: 5 additions & 3 deletions src/components/Apply/Company/ApplicationConfirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Constants from "../../../utils/Constants";

const useStyles = makeStyles((theme) => ({
content: {
textAlgin: "justify",
textAlign: "justify",
},
actions: {
margin: theme.spacing(1),
Expand All @@ -22,14 +22,16 @@ const ApplicationConfirmation = () => {
<CardHeader title="Application Submitted" />
<CardContent className={classes.content}>
<Typography variant="body2">
Application Submitted, you should receive a confirmation email shortly. If not, please contact us:
Application Submitted: You should receive an email containing a confirmation link for your application.
Please confirm it within 10 minutes; otherwise, the link will expire. If you have not received an email,
please contact us at
{" "}
<Link color="secondary" href={`mailto:${Constants.CONTACT_US_EMAIL}`}>
{Constants.CONTACT_US_EMAIL}
</Link>
</Typography>
<Typography variant="body2" className={classes.secondText}>
{"Once you're approved, you will receive an email, and then you can log into NIJobs! "}
{"Once your application is approved, you will receive an email. Then, you can log into NIJobs. "}
Do not forget your password, you will need it on the first login.
</Typography>
</CardContent>
Expand Down
38 changes: 36 additions & 2 deletions src/components/Apply/Company/CompanyApplicationUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { validationRulesGenerator, generalHumanError } from "../../../utils";
import { AuthConstants } from "../../Navbar/Auth/AuthUtils";


export const CompanyApplicationConstants = {
password: AuthConstants.password,
motivation: {
Expand All @@ -19,6 +17,42 @@
const HumanReadableErrors = Object.freeze({
"email-already-exists": "The provided email is already associated to our platform.",
"company-application-duplicate-email": "There is already an application associated with that email.",
"company-application-recently-created": "There is an application created less than 10 minutes ago associated with this email.",
});

const ValidationMessages = Object.freeze({
"success": {
title: "Your application has been validated successfully!",
text: "You should receive a confirmation email shortly. If not, please contact us at ",
},
"invalid-token": {
title: "Error! Application does not exist!",
text: "An error has occurred while validating your application! The application you are trying to validate does not exist.",
},
"expired-token": {
title: "Error! Link has expired!",
text: "An error has occurred while validating your application. The link that was sent to you has expired." +
" You will need to create a new application.",
},
"application-already-validated": {
title: "Application was already validated!",
text: "This application was already validated. ",
},
"account-already-using-email": {
title: "Error! Duplicated Email",
text: "There is already an account with this email, please create an application with another email. ",
},
});

export const getValidationMessage = (description) => {
const errorMsg = { title: "Unexpected Error!", text: "An unexpected error has occurred while validating your application. " };
if (!description) {
return errorMsg;

Check warning on line 50 in src/components/Apply/Company/CompanyApplicationUtils.js

View check run for this annotation

Codecov / codecov/patch

src/components/Apply/Company/CompanyApplicationUtils.js#L50

Added line #L50 was not covered by tests
}
if (typeof ValidationMessages[description] === "object") {
return ValidationMessages[description];
}
return errorMsg;

Check warning on line 55 in src/components/Apply/Company/CompanyApplicationUtils.js

View check run for this annotation

Codecov / codecov/patch

src/components/Apply/Company/CompanyApplicationUtils.js#L55

Added line #L55 was not covered by tests
};

export const getHumanError = (error) => generalHumanError(error, HumanReadableErrors);
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe("Search URL Widget", () => {
const testString = "test-string";

// global.window = Object.create(window);

Object.defineProperty(window, "location", {
value: {
href: testString,
Expand Down
37 changes: 37 additions & 0 deletions src/components/Offers/Form/form-components/OfferForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ import { Controller } from "react-hook-form";
import { useMobile } from "../../../../utils/media-queries";
import "../editor.css";
import ApplyURLComponent from "./ApplyURLComponent";
import { Alert } from "../../../utils/Alert";
import { fetchCompanyApplication } from "../../../../services/companyService";
import useSession from "../../../../hooks/useSession.js";
import { addSnackbar } from "../../../../actions/notificationActions";

export const PAID_OPTIONS = [
{ value: "none", label: "Unspecified" },
{ value: true, label: "Paid" },
{ value: false, label: "Unpaid" },
];


const scrollToError = (errorArray) => {
if (Object.keys(errorArray).length !== 0) {
const element = document.getElementById(Object.keys(errorArray)[0]);
Expand All @@ -55,6 +60,7 @@ const scrollToError = (errorArray) => {
}
};


const OfferForm = ({ context, title }) => {
const {
submit,
Expand Down Expand Up @@ -104,6 +110,25 @@ const OfferForm = ({ context, title }) => {
const Content = isMobile ? DialogContent : CardContent;
const classes = useOfferFormStyles(isMobile)();

const [state, setState] = useState("APPROVED");
const session = useSession();
const companyId = session.data?.company?._id;
useEffect(() => {
if (isAdmin) return;
if (!session.isValidating && session.isLoggedIn) {
fetchCompanyApplication(companyId)
.then((application) => {
setState(application.state);
})
.catch(() => {
addSnackbar({
message: "An unexpected error occurred, please try refreshing the browser window.",
key: `${Date.now()}-fetchCompanyApplicationsError`,
});
});
}
}, [session.isValidating, session.isLoggedIn, isAdmin, companyId]);

const showOwnerComponent = isAdmin && showCompanyField;

const SelectStylingProps = {
Expand All @@ -127,6 +152,18 @@ const OfferForm = ({ context, title }) => {
? <Redirect to={`/offer/${offerId}`} push />
:
<div className={classes.formCard}>

{(state !== "APPROVED") && session.isLoggedIn &&
<Alert
type={"warning"}
fontSize={1.2}
>
{
"This offer will stay hidden from the public until your account is approved!"
}
</Alert>
}

<CardHeader title={!isMobile && title} />
<Content className={classes.formContent}>
<ConnectedLoginAlert
Expand Down
9 changes: 9 additions & 0 deletions src/components/Offers/Form/form-components/offerStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,13 @@ export default (isMobile) => makeStyles((theme) => ({
paddingTop: theme.spacing(2),
marginTop: theme.spacing(2),
},
warning: {
fontSize: "1.2em",
"& .MuiAlert-icon": {
fontSize: "1.5em",
},
marginBottom: "1em",


},
}));
1 change: 0 additions & 1 deletion src/components/Offers/New/CreateOfferForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import React, { useCallback } from "react";
import { parseApplyURL, parseRequestErrors } from "../Form/OfferUtils";
import { newOffer } from "../../../services/offerService";
Expand Down
41 changes: 41 additions & 0 deletions src/components/Offers/New/CreateOfferForm.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { act } from "@testing-library/react";
import { DAY_IN_MS } from "../../../utils/TimeUtils";
import { PAID_OPTIONS } from "../Form/form-components/OfferForm";
import { HumanValidationReasons } from "../../../utils";
import { fetchCompanyApplication } from "../../../services/companyService";

jest.mock("../../../hooks/useSession");
jest.mock("../../../services/locationSearchService");
jest.mock("../../../services/companyService");

// eslint-disable-next-line react/prop-types
const CreateOfferWrapper = ({ children }) => {
Expand All @@ -39,6 +41,8 @@ describe("Create Offer Form", () => {

const initialState = {};
const theme = createTheme({});
// eslint-disable-next-line require-await
fetchCompanyApplication.mockImplementation(async () => "APPROVED");

// it("Should edit description", () => {
// As of today, it is not possible to test contenteditable elements (such as the awesome description editor)
Expand Down Expand Up @@ -216,6 +220,43 @@ describe("Create Offer Form", () => {
expect(element).toBeVisible();
});
});

it("Should render alert if company is not approved", async () => {
useSession.mockImplementation(() => ({ isLoggedIn: true, data: { company: { name: "Company Name" } } }));
// eslint-disable-next-line require-await
fetchCompanyApplication.mockImplementation(async () => ({ state: "PENDING" }));

await renderWithStoreAndTheme(
<BrowserRouter>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<CreateOfferWrapper>
<CreateOfferPage />
</CreateOfferWrapper>
</MuiPickersUtilsProvider>
</BrowserRouter>,
{ initialState, theme }
);
expect(screen.queryByTestId("Alert")).toBeInTheDocument();

});

it("Should not render alert if company is approved", async () => {
useSession.mockImplementation(() => ({ isLoggedIn: true, data: { company: { name: "Company Name" } } }));
// eslint-disable-next-line require-await
fetchCompanyApplication.mockImplementation(async () => ({ state: "APPROVED" }));

await renderWithStoreAndTheme(
<BrowserRouter>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<CreateOfferWrapper>
<CreateOfferPage />
</CreateOfferWrapper>
</MuiPickersUtilsProvider>
</BrowserRouter>,
{ initialState, theme }
);
expect(await screen.queryByTestId("Alert")).not.toBeInTheDocument();
});
});

describe("Should validate Form", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export const columns = {
};

export const ApplicationStateLabel = Object.freeze({
UNVERIFIED: "Unverified",
dsantosferreira marked this conversation as resolved.
Show resolved Hide resolved
APPROVED: "Approved",
PENDING: "Pending",
REJECTED: "Rejected",

});
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const generateApplications = (n, forceState) => {
for (let i = 0; i < n; i++) {
applications.push(generateApplication(
i,
forceState || STATES[i % 3]
forceState || STATES[(i) % 4]
));
}
return applications;
Expand Down Expand Up @@ -272,6 +272,25 @@ describe("Application Review Widget", () => {
.toEqual(`${API_HOSTNAME}/applications/company/${applications[0].id}/reject`, { credentials: "include", method: "POST" });
});

it("Should not have approve and reject buttons in unverified applications", async () => {
const applications = generateApplications(1, "UNVERIFIED");

fetch.mockResponse(JSON.stringify({ applications }));

await act(async () =>
renderWithStoreAndTheme(
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<SnackbarProvider maxSnack={3}>
<Notifier />
<ApplicationsReviewWidget />
</SnackbarProvider>
</MuiPickersUtilsProvider>, { initialState: {}, theme })
);

expect(screen.queryByLabelText("Approve Application")).not.toBeInTheDocument();
expect(screen.queryByLabelText("Reject Application")).not.toBeInTheDocument();
});

it("Should maintain state filter after rejecting an application", async () => {
const applications = generateApplications(1, "PENDING");

Expand Down Expand Up @@ -820,13 +839,13 @@ describe("Application Review Widget", () => {

fireEvent.click(screen.getByLabelText("Filter list"));

fireEvent.change(screen.getByLabelText("Company Name"), { target: { value: "0" } });
fireEvent.change(screen.getByLabelText("Company Name"), { target: { value: "1" } });

fireEvent.change(screen.getByLabelText("Date From..."), { target: { value:
format(new Date(applications[0].submittedAt), "yyyy-MM-dd"),
format(new Date(applications[1].submittedAt), "yyyy-MM-dd"),
} });
fireEvent.change(screen.getByLabelText("Date To..."), { target: { value:
format(new Date(applications[0].submittedAt), "yyyy-MM-dd"),
format(new Date(applications[1].submittedAt), "yyyy-MM-dd"),
} });

await userEvent.click(screen.getByLabelText("State"));
Expand All @@ -837,7 +856,7 @@ describe("Application Review Widget", () => {

expect(screen.getAllByTestId("application-row")
.map((el) => el.querySelector("td:nth-child(2)").textContent)
).toStrictEqual([applications[0].companyName]);
).toStrictEqual([applications[1].companyName]);

fireEvent.click(screen.getByLabelText("Filter list"));

Expand Down
37 changes: 37 additions & 0 deletions src/components/utils/Alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

import { Alert as MUIAlert, AlertTitle } from "@material-ui/lab";
import { makeStyles } from "@material-ui/core";
import PropTypes from "prop-types";
import { Warning as WarningIcon } from "@material-ui/icons";
import React from "react";

const useStyles = (props) => makeStyles(() => ({

content: {
fontSize: `${props.fontSize}em`,
"& .MuiAlert-icon": {
fontSize: `${props.fontSize + 0.3}em`,
},
margin: "0.5em 0em",
},
}));

export const Alert = ({ type, title, fontSize = 1, children }) => {
const classes = useStyles({ fontSize: fontSize })();
return (
<MUIAlert severity={type} className={classes.content} icon={<WarningIcon />} data-testid="Alert">
{title ?
<AlertTitle>

Check warning on line 24 in src/components/utils/Alert.js

View check run for this annotation

Codecov / codecov/patch

src/components/utils/Alert.js#L24

Added line #L24 was not covered by tests
{title}
</AlertTitle> : null}
{children}
</MUIAlert>
);
};

Alert.propTypes = {
type: PropTypes.oneOf(["error", "warning", "info", "success"]),
title: PropTypes.string,
children: PropTypes.string,
fontSize: PropTypes.number,
};
Loading
Loading