diff --git a/README.md b/README.md index 7f4a3c5..d3ca2cc 100644 --- a/README.md +++ b/README.md @@ -530,6 +530,9 @@ Key Functionalities Include:  +- Edit an Application Date: Job application dates can also be edited directly on the job application page by clicking on the "Applied On" date to pull up an interactive calendar, and cliking on a new date. + +
@@ -553,6 +556,10 @@ FORMAT: lastname, firstname - [Github](https://github.com/stefanjbloom) - [LinkedIn](https://www.linkedin.com/in/stefanjbloom/) +**Cardona, Danielle** +- [Github](https://github.com/dcardona23) +- [LinkedIn](www.linkedin.com/in/danielle-cardona-se) + **Chirchirillo, Joe** - [Github](https://github.com/jchirch) diff --git a/cypress/e2e/jobsApplicationSpec.cy.js b/cypress/e2e/jobsApplicationSpec.cy.js index bf323d0..9b5f1f2 100644 --- a/cypress/e2e/jobsApplicationSpec.cy.js +++ b/cypress/e2e/jobsApplicationSpec.cy.js @@ -197,13 +197,13 @@ describe("View specific job app page with all fields filled in", () => { cy.wait("@showSingleJobApp"); - cy.get('h1.text-cyan-600').should("have.text", "Backend Developer"); - cy.get('h2.text-cyan-600').first().should("have.text", "Creative Solutions Inc."); + cy.get('h1.text-cyan-600').should("have.text", "Backend Developer") + .next().should("have.text", "Creative Solutions Inc."); }); it("navigates to the company details page", () => { cy.wait("@showSingleJobApp"); - cy.get('h2.text-cyan-600').first().click() + cy.get('h2').contains("Creative Solutions Inc.").click() cy.wait("@getCompanyDetails") cy.location('pathname').should('match', /\/companies\/3\/contacts$/) @@ -211,12 +211,9 @@ describe("View specific job app page with all fields filled in", () => { it("displays the correct company details", () => { cy.wait("@showSingleJobApp"); - cy.get('h2.text-cyan-600').first().click() + cy.get('h2').contains("Creative Solutions Inc.").click() cy.wait("@getCompanyDetails") cy.get("h1").should("have.text", "Company Details"); - - cy.get("h2").contains("Company Name:") - .next().should("have.text", "Creative Solutions Inc."); cy.get("h2").contains("Website:") .next().should("have.text", "https://creativesolutions.com"); @@ -240,17 +237,13 @@ describe("View specific job app page with all fields filled in", () => { cy.wait("@showSingleJobApp"); - cy.get("p.font-medium") + cy.get("p.font-bold") .should("contain.text", "Applied On") - .within(() => { - cy.get("span.font-semibold").should("have.text", "2024-08-20"); {/* REFACTOR AWAITING UPDATE JOB APP ROUTE */} - }); + cy.get('[data-testid="application-date"]').should("have.text", "August 20, 2024"); {/* REFACTOR AWAITING UPDATE JOB APP ROUTE */} cy.get("p.mb-6") .should("contain.text", "Status:") - .within(() => { - cy.get("span").should("have.text", "Interviewing");{/* REFACTOR AWAITING UPDATE JOB APP ROUTE */} - }); + cy.get('[data-testid="job-status"]').should("have.text", "Interviewing");{/* REFACTOR AWAITING UPDATE JOB APP ROUTE */} }); it("displays notes and edit button", () => { @@ -499,6 +492,28 @@ describe("Editability of specific job application fields", () => { cy.get("tbody > tr").contains("Creative").click(); }); + it("edits an application date", () => { + cy.intercept("PATCH", "http://localhost:3001/api/v1/users/1/job_applications/3", (req) => { + console.log(req.body) + req.on("response", (res) => { + }); + req.reply({ + statusCode: 200, + fixture: "mockJobAppDateUpdate", + headers: { + "Content-Type": "application/json", + }, + }); + }).as("updateJobAppDate"); + + cy.wait("@showSingleJobApp"); + cy.get('[data-testid="application-date"]').click() + cy.get('[aria-label="Choose Thursday, August 1st, 2024"]').click() + cy.get('[data-testid="application-date"]').should('have.text', 'August 1, 2024') + cy.wait("@updateJobAppDate") + cy.get('[data-testid="application-date"]').should('have.text', 'August 1, 2024') + }) + it("Should display the edit model when edit button is clicked", () => { cy.get('[data-testid="edit-modal"]').should('not.exist'); cy.get('[data-testid="edit-modal-title"]').should('not.exist'); @@ -537,7 +552,7 @@ describe("Editability of specific job application fields", () => { "Content-Type": "application/json", }, }); - }).as("showSingleJobAppEmptyFields"); + }).as("updateJobApp"); cy.get('[data-testid="edit-button"]').click() @@ -548,6 +563,15 @@ describe("Editability of specific job application fields", () => { cy.get('[data-testid="edit-modal-form-notes"]').clear().type("Talked with recruiter, sounds like a great opportunity to learn new things"); cy.get('[data-testid="edit-modal-form-submit-button"]').click(); + cy.reload(); + cy.get("#email").type("danny_de@email.com"); + cy.get("#password").type("jerseyMikesRox7"); + cy.get('[data-testid="login-button"]').click(); + cy.get('[data-testid="applications-iconD"]').click(); + cy.get("tbody > tr").contains("Creative").click(); + + cy.wait("@updateJobApp"); + cy.get('[data-testid="job-Title"]').should('contain', 'Frontend Developer') cy.get('[data-testid="job-companyName"]').should('contain', 'Creative Solutions Inc.') cy.get('[data-testid="job-status"]').should("have.text", "Offer") @@ -569,8 +593,20 @@ describe("Editability of specific job application fields", () => { }, }); }).as("showSingleJobAppEmptyFields"); + cy.get('[data-testid="edit-button"]').click() cy.get('[data-testid="edit-modal-form-submit-button"]').click(); + cy.wait("@showSingleJobAppEmptyFields"); + + cy.reload() + cy.get("#email").type("danny_de@email.com") + cy.get("#password").type("jerseyMikesRox7") + cy.get('[data-testid="login-button"]').click(); + cy.get('[data-testid="applications-iconD"]').click(); + cy.get("tbody > tr").contains("Creative").click(); + + cy.wait("@getJobApplications"); + cy.wait("@showSingleJobApp") cy.get('[data-testid="job-Title"]').should('contain', 'Backend Developer') cy.get('[data-testid="job-companyName"]').should('contain', 'Creative Solutions Inc.') diff --git a/cypress/fixtures/mockJobAppDateUpdate.json b/cypress/fixtures/mockJobAppDateUpdate.json new file mode 100644 index 0000000..903d96a --- /dev/null +++ b/cypress/fixtures/mockJobAppDateUpdate.json @@ -0,0 +1,26 @@ +{ + "data": { + "id": "3", + "type": "job_application", + "attributes": { + "position_title": "Backend Developer", + "date_applied": "2024-08-01", + "status": 2, + "notes": "Had a technical interview, awaiting decision.", + "job_description": "Developing RESTful APIs and optimizing server performance.", + "application_url": "https://creativesolutions.com/careers/backend-developer", + "company_id": 3, + "company_name": "Creative Solutions Inc.", + "contacts": [ + { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson", + "email": "michael.johnson@example.com", + "phone_number": "123-555-9012", + "notes": "Hiring manager at Creative Solutions Inc." + } + ] + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fddf0fb..4883eca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,8 +17,10 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "autoprefixer": "^10.4.20", + "moment-timezone": "^0.5.47", "postcss": "^8.4.49", "react": "^18.3.1", + "react-datepicker": "^8.0.0", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", @@ -2582,6 +2584,59 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.4.tgz", + "integrity": "sha512-05mXdkUiVh8NCEcYKQ2C9SV9IkZ9k/dFtYmaEIN2riLv80UHoXylgBM76cgPJYfLJM3dJz7UE5MOVH0FypMd2Q==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@fontsource/roboto": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.1.0.tgz", @@ -7617,6 +7672,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -13733,6 +13798,27 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.47", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.47.tgz", + "integrity": "sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -15961,6 +16047,21 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-datepicker": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-8.0.0.tgz", + "integrity": "sha512-OmWkFx3BGPXQhBdhFCZyfqR6n2Z5T3WaEXQxz0tdTY6zNntklnIDkaDSYsbKwy7TcyBgeoneG5f4sCwmFPJ4eA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.27.3", + "clsx": "^2.1.1", + "date-fns": "^4.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -18117,6 +18218,12 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "3.4.16", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz", diff --git a/package.json b/package.json index b63c025..8645b7d 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,10 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "autoprefixer": "^10.4.20", + "moment-timezone": "^0.5.47", "postcss": "^8.4.49", "react": "^18.3.1", + "react-datepicker": "^8.0.0", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", diff --git a/src/assets/editJobAppDate.png b/src/assets/editJobAppDate.png new file mode 100644 index 0000000..c5d588d Binary files /dev/null and b/src/assets/editJobAppDate.png differ diff --git a/src/components/pages/showJobApplication.tsx b/src/components/pages/showJobApplication.tsx index d0af849..6c0582b 100644 --- a/src/components/pages/showJobApplication.tsx +++ b/src/components/pages/showJobApplication.tsx @@ -3,6 +3,8 @@ import { useParams, Link } from "react-router-dom"; import { showJobApp, updateJobApplication } from "../../trackerApiCalls"; import { useUserLoggedContext } from "../../context/UserLoggedContext"; import { statusMap, statusStyles} from "../JobApplicationUtilities"; +import DatePicker from 'react-datepicker' +import moment from 'moment-timezone' interface Contact { id: number; @@ -15,7 +17,7 @@ interface Contact { interface JobApplicationAttributes { position_title: string; - date_applied: Date; + date_applied: string; status: number; notes: string; job_description: string; @@ -46,7 +48,9 @@ function JobApplication() { const [notes, setNotes] = useState(''); const [jobDescription, setJobDescription] = useState(''); const [applicationURL, setApplicationURL] = useState(''); + const [dateApplied, setDateApplied] = useState(''); const [companyId, setCompanyId] = useState(''); + const [isEditing, setIsEditing] = useState(false); useEffect(() => { if (jobAppId) { @@ -55,6 +59,7 @@ function JobApplication() { const id = parseInt(jobAppId, 10); if (isNaN(id)) throw new Error("Invalid jobAppId."); const data = await showJobApp(user.data.id, id, token); + setJobApp(data.data.attributes as JobApplicationAttributes); setPositionTitle(data.data.attributes.position_title) setStatus(data.data.attributes.status) @@ -62,6 +67,7 @@ function JobApplication() { setJobDescription(data.data.attributes.job_description) setApplicationURL(data.data.attributes.application_url) setCompanyId(data.data.attributes.company_id) + setDateApplied(moment(data.data.attributes.date_applied).local().format("YYYY-MM-DD")) } catch (err) { console.error("Failed to fetch job application:", err); @@ -78,8 +84,12 @@ function JobApplication() { const openEdit = () => setIsEditModelOpen(true); const closeEdit = () => setIsEditModelOpen(false); - const handleSubmit = (event: React.FormEvent{error}
} @@ -113,25 +129,54 @@ function JobApplication() { data-testid="job-Title"> {jobApp.position_title} - -- Applied On:{" "} - - {`${jobApp.date_applied}`} - -
-- Status:{" "} - - {statusMap[jobApp.status]} - -
++ Applied On: {" "} +
+ {isEditing ? ( ++ Status:{" "} + + {statusMap[jobApp.status]} + +
+{jobApp.notes ? jobApp.notes : "Click edit to add some notes."}