From 9bf50c12800458f6430ac9f0ef85667b03a20a3c Mon Sep 17 00:00:00 2001 From: Simon Broberg Date: Thu, 14 Dec 2023 14:33:13 -0500 Subject: [PATCH] feat: update data and dependencies --- app/components/ClientWork/index.ts | 1 - .../ExternalWork.test.tsx} | 32 +- .../ExternalWork.tsx} | 28 +- app/components/ExternalWork/index.ts | 1 + .../FormTextField/FormTextField.tsx | 9 +- .../SearchFlickrFetch.disabledtest.tsx | 288 ++++ .../SearchFlickr/SearchFlickrFetch.test.tsx | 287 ---- app/components/Welcome/Welcome.test.tsx | 3 +- app/components/Welcome/Welcome.tsx | 3 +- app/data/clientWork.ts | 18 - app/data/externalWork.ts | 29 + app/data/projects.ts | 6 +- app/data/skills.ts | 16 +- app/features/Profile/Profile.test.tsx | 24 +- app/features/Profile/Profile.tsx | 30 +- app/images/recipes.jpg | Bin 0 -> 224341 bytes app/layout.tsx | 2 +- app/utilities.tsx | 2 + package-lock.json | 1309 +++++++++-------- package.json | 51 +- 20 files changed, 1112 insertions(+), 1027 deletions(-) delete mode 100644 app/components/ClientWork/index.ts rename app/components/{ClientWork/ClientWork.test.tsx => ExternalWork/ExternalWork.test.tsx} (69%) rename app/components/{ClientWork/ClientWork.tsx => ExternalWork/ExternalWork.tsx} (64%) create mode 100644 app/components/ExternalWork/index.ts create mode 100644 app/components/SearchFlickr/SearchFlickrFetch.disabledtest.tsx delete mode 100644 app/components/SearchFlickr/SearchFlickrFetch.test.tsx delete mode 100644 app/data/clientWork.ts create mode 100644 app/data/externalWork.ts create mode 100644 app/images/recipes.jpg diff --git a/app/components/ClientWork/index.ts b/app/components/ClientWork/index.ts deleted file mode 100644 index f0a8e6e..0000000 --- a/app/components/ClientWork/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ClientWork } from "./ClientWork"; diff --git a/app/components/ClientWork/ClientWork.test.tsx b/app/components/ExternalWork/ExternalWork.test.tsx similarity index 69% rename from app/components/ClientWork/ClientWork.test.tsx rename to app/components/ExternalWork/ExternalWork.test.tsx index 37990d4..86dbb14 100644 --- a/app/components/ClientWork/ClientWork.test.tsx +++ b/app/components/ExternalWork/ExternalWork.test.tsx @@ -1,38 +1,38 @@ import { render, screen } from "@testing-library/react"; import { userEvent } from "@testing-library/user-event"; -import { ClientWork } from "app/components/ClientWork/ClientWork"; -import { CLIENT_WORK } from "app/data/clientWork"; +import { ExternalWork } from "app/components/ExternalWork/ExternalWork"; +import { EXTERNAL_WORK } from "app/data/externalWork"; -describe("test ClientWork", () => { +describe("test ExternalWork", () => { test("renders all the tabs with the correct label", () => { - render(); + render(); - CLIENT_WORK.forEach(({ name }) => { + EXTERNAL_WORK.forEach(({ name }) => { expect(screen.getByRole("tab", { name })).toBeInTheDocument(); }); }); test("only renders a single tab panel", () => { - render(); + render(); expect(screen.getAllByRole("tabpanel")).toHaveLength(1); }); test("renders the first tab panel initially", () => { - render(); + render(); expect( - screen.getByRole("tabpanel", { name: CLIENT_WORK[0].name }), + screen.getByRole("tabpanel", { name: EXTERNAL_WORK[0].name }), ).toBeInTheDocument(); }); test("renders the correct tab panel after the its tab is clicked", () => { const user = userEvent.setup(); - render(); + render(); - CLIENT_WORK.forEach(async ({ name }) => { + EXTERNAL_WORK.forEach(async ({ name }) => { await user.click( screen.getByRole("tab", { name, @@ -46,9 +46,9 @@ describe("test ClientWork", () => { test("renders the correct image for each tab panel", () => { const user = userEvent.setup(); - render(); + render(); - CLIENT_WORK.forEach(async ({ image, name }) => { + EXTERNAL_WORK.forEach(async ({ image, name }) => { await user.click( screen.getByRole("tab", { name, @@ -62,9 +62,9 @@ describe("test ClientWork", () => { test("renders the correct description for each tab panel", () => { const user = userEvent.setup(); - render(); + render(); - CLIENT_WORK.forEach(async ({ description, name }) => { + EXTERNAL_WORK.forEach(async ({ description, name }) => { await user.click( screen.getByRole("tab", { name, @@ -78,9 +78,9 @@ describe("test ClientWork", () => { test("renders the correct link for each tab panel", () => { const user = userEvent.setup(); - render(); + render(); - CLIENT_WORK.forEach(async ({ href, name }) => { + EXTERNAL_WORK.forEach(async ({ href, name }) => { await user.click( screen.getByRole("tab", { name, diff --git a/app/components/ClientWork/ClientWork.tsx b/app/components/ExternalWork/ExternalWork.tsx similarity index 64% rename from app/components/ClientWork/ClientWork.tsx rename to app/components/ExternalWork/ExternalWork.tsx index e7700cb..adaf8cb 100644 --- a/app/components/ClientWork/ClientWork.tsx +++ b/app/components/ExternalWork/ExternalWork.tsx @@ -9,22 +9,22 @@ import { Tabs, } from "@mui/material"; -import { CLIENT_WORK } from "app/data/clientWork"; +import { EXTERNAL_WORK } from "app/data/externalWork"; import { useTabs } from "app/hooks/useTabs"; import { computeTabAndPanelProps } from "app/utilities"; -export function ClientWork() { +export function ExternalWork() { const { handleChangeTab, tab } = useTabs(); return ( <> - {CLIENT_WORK.map(({ name }) => ( + {EXTERNAL_WORK.map(({ name }) => ( ))} - {CLIENT_WORK.map(({ description, href, image, name }, index) => + {EXTERNAL_WORK.map(({ description, href, image, name }, index) => tab === index ? ( @@ -36,15 +36,17 @@ export function ClientWork() { - + {href ? ( + + ) : null} ) : null, diff --git a/app/components/ExternalWork/index.ts b/app/components/ExternalWork/index.ts new file mode 100644 index 0000000..14e7f4c --- /dev/null +++ b/app/components/ExternalWork/index.ts @@ -0,0 +1 @@ +export { ExternalWork } from "./ExternalWork"; diff --git a/app/components/FormTextField/FormTextField.tsx b/app/components/FormTextField/FormTextField.tsx index 5bc4266..1cd789c 100644 --- a/app/components/FormTextField/FormTextField.tsx +++ b/app/components/FormTextField/FormTextField.tsx @@ -60,7 +60,14 @@ export function FormTextField({ ...rest }: FormTextFieldProps) { const { - field: { onChange: reactHookFormOnChange, ref, value, ...field }, + field: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + disabled: isReactHookFormFieldDisabled, + onChange: reactHookFormOnChange, + ref, + value, + ...field + }, fieldState: { error }, formState: { isSubmitting }, } = useController({ diff --git a/app/components/SearchFlickr/SearchFlickrFetch.disabledtest.tsx b/app/components/SearchFlickr/SearchFlickrFetch.disabledtest.tsx new file mode 100644 index 0000000..9afb91a --- /dev/null +++ b/app/components/SearchFlickr/SearchFlickrFetch.disabledtest.tsx @@ -0,0 +1,288 @@ +// import { +// render, +// screen, +// waitForElementToBeRemoved, +// } from "@testing-library/react"; +// import { userEvent } from "@testing-library/user-event"; +// import { rest } from "msw"; +// import { setupServer } from "msw/node"; + +// import { SearchFlickr } from "app/components/SearchFlickr"; +// import { +// ERROR_MESSAGE, +// LOADING_MESSAGE, +// NO_RESULTS_FOUND_MESSAGE, +// } from "app/components/SearchFlickr/useSearchFlickr"; + +// describe("mock API calls", () => { +// const API_URL = "https://www.flickr.com/services/rest/*"; +// const MOCK_NO_RESULTS_FOUND_RESPONSE = { +// photos: { +// page: 1, +// pages: 0, +// perpage: 12, +// photo: [], +// total: 0, +// }, +// stat: "ok", +// }; +// const MOCK_SUCCESS_RESPONSE = { +// photos: { +// page: 1, +// pages: 7232, +// perpage: 12, +// photo: [ +// { +// farm: 66, +// id: "53172913670", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "145904679@N04", +// secret: "ea02b7f0fb", +// server: "65535", +// title: "XE3B6092 - Río Namorona - Namorona river (Madagascar)", +// }, +// { +// farm: 66, +// id: "53172133239", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "184547423@N07", +// secret: "1d412383b8", +// server: "65535", +// title: "Fog starting to come in", +// }, +// { +// farm: 66, +// id: "53171347032", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "184547423@N07", +// secret: "a9fa45537e", +// server: "65535", +// title: "Moonglow over a fishing boat", +// }, +// { +// farm: 66, +// id: "53169912700", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "41768085@N05", +// secret: "f81abd69dc", +// server: "65535", +// title: "Chinook canoe", +// }, +// { +// farm: 66, +// id: "53169631188", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "f73b92690d", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 1", +// }, +// { +// farm: 66, +// id: "53168554607", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "ecc55e5c11", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 2", +// }, +// { +// farm: 66, +// id: "53169341979", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "01944669ce", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 3", +// }, +// { +// farm: 66, +// id: "53169579405", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "94acc6b99e", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 4", +// }, +// { +// farm: 66, +// id: "53169630363", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "ae66867f44", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 5", +// }, +// { +// farm: 66, +// id: "53169579135", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "2e12f94cbd", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 6", +// }, +// { +// farm: 66, +// id: "53169629833", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "dc1dc6c3a0", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 7", +// }, +// { +// farm: 66, +// id: "53169629663", +// isfamily: 0, +// isfriend: 0, +// ispublic: 1, +// owner: "95070668@N02", +// secret: "4f85dcb3f1", +// server: "65535", +// title: "Old Felixstowe & Felixstowe Ferry 8", +// }, +// ], +// total: 86775, +// }, +// stat: "ok", +// }; +// const server = setupServer( +// rest.get(API_URL, (request, response, context) => +// response(context.json(MOCK_SUCCESS_RESPONSE)), +// ), +// ); + +// function useDelayedResponse() { +// return server.use( +// rest.get(API_URL, (request, response, context) => +// response(context.delay(100), context.json(MOCK_SUCCESS_RESPONSE)), +// ), +// ); +// } + +// function useErrorResponse() { +// return server.use( +// rest.get(API_URL, (request, response, context) => +// response(context.status(500)), +// ), +// ); +// } + +// function useNoResultsFoundResponse() { +// return server.use( +// rest.get(API_URL, (request, response, context) => +// response(context.json(MOCK_NO_RESULTS_FOUND_RESPONSE)), +// ), +// ); +// } + +// beforeAll(() => server.listen()); +// afterEach(() => server.resetHandlers()); +// afterAll(() => server.close()); + +// test("renders the correct error message", async () => { +// const user = userEvent.setup(); + +// useErrorResponse(); +// render(); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(screen.getByRole("button")); +// await screen.findByText(ERROR_MESSAGE, { exact: false }); +// expect( +// screen.getByText(ERROR_MESSAGE, { exact: false }), +// ).toBeInTheDocument(); +// }); + +// test("renders the correct no results found message", async () => { +// const user = userEvent.setup(); + +// useNoResultsFoundResponse(); +// render(); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(screen.getByRole("button")); +// await screen.findByText(NO_RESULTS_FOUND_MESSAGE); +// expect(screen.getByText(NO_RESULTS_FOUND_MESSAGE)).toBeInTheDocument(); +// }); + +// test("textbox is disabled while submitting", async () => { +// const user = userEvent.setup(); + +// useDelayedResponse(); +// render(); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(screen.getByRole("button")); +// expect(screen.getByRole("textbox")).toBeDisabled(); +// }); + +// test("button is disabled while submitting", async () => { +// const user = userEvent.setup(); + +// useDelayedResponse(); +// render(); +// const button = screen.getByRole("button"); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(button); +// expect(button).toBeDisabled(); +// }); + +// test("renders the correct loading message", async () => { +// const user = userEvent.setup(); + +// useDelayedResponse(); +// render(); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(screen.getByRole("button")); +// expect(screen.getByText(LOADING_MESSAGE)).toBeInTheDocument(); +// }); + +// test("renders all the images returned from Flickr", async () => { +// const user = userEvent.setup(); + +// useDelayedResponse(); +// render(); + +// await user.type(screen.getByRole("textbox"), "canoes"); +// await user.click(screen.getByRole("button")); +// await waitForElementToBeRemoved(screen.queryByText(LOADING_MESSAGE)); + +// MOCK_SUCCESS_RESPONSE.photos.photo.forEach( +// ({ id, secret, server, title }) => { +// const sizeSuffix = "q"; + +// expect(screen.getByRole("img", { name: title })).toHaveAttribute( +// "src", +// `https://live.staticflickr.com/ +// ${server}/${id}_${secret}_${sizeSuffix}.jpg`, +// ); +// }, +// ); +// }); +// }); diff --git a/app/components/SearchFlickr/SearchFlickrFetch.test.tsx b/app/components/SearchFlickr/SearchFlickrFetch.test.tsx deleted file mode 100644 index f904d0a..0000000 --- a/app/components/SearchFlickr/SearchFlickrFetch.test.tsx +++ /dev/null @@ -1,287 +0,0 @@ -import { - render, - screen, - waitForElementToBeRemoved, -} from "@testing-library/react"; -import { userEvent } from "@testing-library/user-event"; -import { rest } from "msw"; -import { setupServer } from "msw/node"; - -import { SearchFlickr } from "app/components/SearchFlickr"; -import { - ERROR_MESSAGE, - LOADING_MESSAGE, - NO_RESULTS_FOUND_MESSAGE, -} from "app/components/SearchFlickr/useSearchFlickr"; - -describe("mock API calls", () => { - const API_URL = "https://www.flickr.com/services/rest/*"; - const MOCK_NO_RESULTS_FOUND_RESPONSE = { - photos: { - page: 1, - pages: 0, - perpage: 12, - photo: [], - total: 0, - }, - stat: "ok", - }; - const MOCK_SUCCESS_RESPONSE = { - photos: { - page: 1, - pages: 7232, - perpage: 12, - photo: [ - { - farm: 66, - id: "53172913670", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "145904679@N04", - secret: "ea02b7f0fb", - server: "65535", - title: "XE3B6092 - Río Namorona - Namorona river (Madagascar)", - }, - { - farm: 66, - id: "53172133239", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "184547423@N07", - secret: "1d412383b8", - server: "65535", - title: "Fog starting to come in", - }, - { - farm: 66, - id: "53171347032", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "184547423@N07", - secret: "a9fa45537e", - server: "65535", - title: "Moonglow over a fishing boat", - }, - { - farm: 66, - id: "53169912700", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "41768085@N05", - secret: "f81abd69dc", - server: "65535", - title: "Chinook canoe", - }, - { - farm: 66, - id: "53169631188", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "f73b92690d", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 1", - }, - { - farm: 66, - id: "53168554607", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "ecc55e5c11", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 2", - }, - { - farm: 66, - id: "53169341979", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "01944669ce", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 3", - }, - { - farm: 66, - id: "53169579405", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "94acc6b99e", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 4", - }, - { - farm: 66, - id: "53169630363", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "ae66867f44", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 5", - }, - { - farm: 66, - id: "53169579135", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "2e12f94cbd", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 6", - }, - { - farm: 66, - id: "53169629833", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "dc1dc6c3a0", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 7", - }, - { - farm: 66, - id: "53169629663", - isfamily: 0, - isfriend: 0, - ispublic: 1, - owner: "95070668@N02", - secret: "4f85dcb3f1", - server: "65535", - title: "Old Felixstowe & Felixstowe Ferry 8", - }, - ], - total: 86775, - }, - stat: "ok", - }; - const server = setupServer( - rest.get(API_URL, (request, response, context) => - response(context.json(MOCK_SUCCESS_RESPONSE)), - ), - ); - - function useDelayedResponse() { - return server.use( - rest.get(API_URL, (request, response, context) => - response(context.delay(100), context.json(MOCK_SUCCESS_RESPONSE)), - ), - ); - } - - function useErrorResponse() { - return server.use( - rest.get(API_URL, (request, response, context) => - response(context.status(500)), - ), - ); - } - - function useNoResultsFoundResponse() { - return server.use( - rest.get(API_URL, (request, response, context) => - response(context.json(MOCK_NO_RESULTS_FOUND_RESPONSE)), - ), - ); - } - - beforeAll(() => server.listen()); - afterEach(() => server.resetHandlers()); - afterAll(() => server.close()); - - test("renders the correct error message", async () => { - const user = userEvent.setup(); - - useErrorResponse(); - render(); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(screen.getByRole("button")); - await screen.findByText(ERROR_MESSAGE, { exact: false }); - expect( - screen.getByText(ERROR_MESSAGE, { exact: false }), - ).toBeInTheDocument(); - }); - - test("renders the correct no results found message", async () => { - const user = userEvent.setup(); - - useNoResultsFoundResponse(); - render(); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(screen.getByRole("button")); - await screen.findByText(NO_RESULTS_FOUND_MESSAGE); - expect(screen.getByText(NO_RESULTS_FOUND_MESSAGE)).toBeInTheDocument(); - }); - - test("textbox is disabled while submitting", async () => { - const user = userEvent.setup(); - - useDelayedResponse(); - render(); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(screen.getByRole("button")); - expect(screen.getByRole("textbox")).toBeDisabled(); - }); - - test("button is disabled while submitting", async () => { - const user = userEvent.setup(); - - useDelayedResponse(); - render(); - const button = screen.getByRole("button"); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(button); - expect(button).toBeDisabled(); - }); - - test("renders the correct loading message", async () => { - const user = userEvent.setup(); - - useDelayedResponse(); - render(); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(screen.getByRole("button")); - expect(screen.getByText(LOADING_MESSAGE)).toBeInTheDocument(); - }); - - test("renders all the images returned from Flickr", async () => { - const user = userEvent.setup(); - - useDelayedResponse(); - render(); - - await user.type(screen.getByRole("textbox"), "canoes"); - await user.click(screen.getByRole("button")); - await waitForElementToBeRemoved(screen.queryByText(LOADING_MESSAGE)); - - MOCK_SUCCESS_RESPONSE.photos.photo.forEach( - ({ id, secret, server, title }) => { - const sizeSuffix = "q"; - - expect(screen.getByRole("img", { name: title })).toHaveAttribute( - "src", - `https://live.staticflickr.com/${server}/${id}_${secret}_${sizeSuffix}.jpg`, - ); - }, - ); - }); -}); diff --git a/app/components/Welcome/Welcome.test.tsx b/app/components/Welcome/Welcome.test.tsx index f4f6df3..da67eaf 100644 --- a/app/components/Welcome/Welcome.test.tsx +++ b/app/components/Welcome/Welcome.test.tsx @@ -1,8 +1,9 @@ import { render, screen } from "@testing-library/react"; -import { JOB_TITLE, Welcome } from "app/components/Welcome/Welcome"; +import { Welcome } from "app/components/Welcome/Welcome"; import welcome from "app/images/welcome.jpg"; import { setupIntersectionObserverMock } from "app/testUtilities"; +import { JOB_TITLE } from "app/utilities"; describe("test Welcome", () => { beforeEach(() => { diff --git a/app/components/Welcome/Welcome.tsx b/app/components/Welcome/Welcome.tsx index ad2b152..71b9742 100644 --- a/app/components/Welcome/Welcome.tsx +++ b/app/components/Welcome/Welcome.tsx @@ -3,8 +3,7 @@ import { Slide } from "react-awesome-reveal"; import { useSmAndUp } from "app/hooks/useSmAndUp"; import welcome from "app/images/welcome.jpg"; - -export const JOB_TITLE = "Fullstack Developer"; +import { JOB_TITLE } from "app/utilities"; export function Welcome() { const { isSmAndUp } = useSmAndUp(); diff --git a/app/data/clientWork.ts b/app/data/clientWork.ts deleted file mode 100644 index 2f06f6b..0000000 --- a/app/data/clientWork.ts +++ /dev/null @@ -1,18 +0,0 @@ -import gameTrainer from "app/images/GameTrainer.jpg"; -import gtd from "app/images/gtd.jpg"; - -export const CLIENT_WORK = [ - { - description: - "A landing page designed and developed for a gaming company, as a portal entrance for Skillsoft's training software. It uses a 3D layering library called Parallax that responds to your movement.", - href: "https://brostack.net/game", - image: gameTrainer, - name: "Game Trainer", - }, - { - description: "A sample landing page developed for a photography company.", - href: "https://brostack.net/gtd", - image: gtd, - name: "gtd.", - }, -]; diff --git a/app/data/externalWork.ts b/app/data/externalWork.ts new file mode 100644 index 0000000..9387e65 --- /dev/null +++ b/app/data/externalWork.ts @@ -0,0 +1,29 @@ +import gameTrainer from "app/images/GameTrainer.jpg"; +import gtd from "app/images/gtd.jpg"; +import recipes from "app/images/recipes.jpg"; + +export const EXTERNAL_WORK = [ + { + description: + "A web application featuring user and recipe management capabilities. This project serves as a testament to my proficiency as a fullstack developer, showcasing expertise in utilizing Next.js and commonly associated technologies.", + href: "https://brostack.net/game", + image: recipes, + name: "Recipe Keeper", + }, + { + description: + "A meticulously crafted landing page tailored for a gaming company, strategically designed and developed to serve as the gateway for Skillsoft's training software. The page comes to life with the incorporation of a dynamic 3D layering library, Parallax, seamlessly responding to user movements, enhancing the overall user experience.", + // href: "https://brostack.net/game", + // TODO: Fix link + image: gameTrainer, + name: "Game Trainer", + }, + { + description: + "A sample landing page developed for a photography company, designed to showcase the artistic prowess and services offered. This visually appealing web page is thoughtfully crafted to provide visitors with an immersive glimpse into the company's portfolio, fostering engagement and leaving a lasting impression.", + // href: "https://brostack.net/gtd", + // TODO: Fix link + image: gtd, + name: "gtd.", + }, +]; diff --git a/app/data/projects.ts b/app/data/projects.ts index d2af434..dc960fa 100644 --- a/app/data/projects.ts +++ b/app/data/projects.ts @@ -1,5 +1,5 @@ import { ButtonAnimation } from "app/components/ButtonAnimation"; -import { ClientWork } from "app/components/ClientWork"; +import { ExternalWork } from "app/components/ExternalWork"; import { InspirationalPosterGenerator } from "app/components/InspirationalPosterGenerator"; import { NumberGuesser } from "app/components/NumberGuesser"; import { SearchFlickr } from "app/components/SearchFlickr"; @@ -27,7 +27,7 @@ export const PROJECTS = [ title: "Scoreboard" as const, }, { - componentName: ClientWork, - title: "Client Work" as const, + componentName: ExternalWork, + title: "External Work" as const, }, ]; diff --git a/app/data/skills.ts b/app/data/skills.ts index 8bb0bcd..20ac945 100644 --- a/app/data/skills.ts +++ b/app/data/skills.ts @@ -23,12 +23,18 @@ export const SKILLS = { "Large Forms", "Dashboards", "Graphs", + // TODO: Add abbreviations for REST, API, HTTP, + // and other items that are partially abbreviations "REST API Development", "API HTTP Requests", "Automated Unit Testing", { text: "CSR", title: "Client Side Rendering" }, { text: "SSR", title: "Server Side Rendering" }, { text: "SSG", title: "Static Site Generation" }, + { + text: "CI/CD", + title: "Continuous Integration/Continuous Development", + }, "Database Design", { text: "ERD", title: "Entity Relationship Diagrams" }, "Database Management", @@ -44,18 +50,23 @@ export const SKILLS = { { text: "CSS", title: "Cascading Tile Sheets" }, "Bootstrap", "JavaScript", - "JQuery", + "jQuery", "TypeScript", "Next", + "NextAuth", "React", "React DOM", "React Router DOM", + "DOMPurify", "Redux", "RTK Query", "React Hook Form", "React Window", + "React Awesome Reveal", + "React Quill", "Jest", "React Testing Library", + { text: "MSW", title: "Mock Service Worker" }, "Vue", "VueX", "Vuetify", @@ -65,6 +76,9 @@ export const SKILLS = { "Ajax", "Axios", "SQL", + "PostgreSQL", + "Prisma", + "Zod", ], name: "Languages" as const, }, diff --git a/app/features/Profile/Profile.test.tsx b/app/features/Profile/Profile.test.tsx index 7eda41b..7d2a2ae 100644 --- a/app/features/Profile/Profile.test.tsx +++ b/app/features/Profile/Profile.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from "@testing-library/react"; -import { Profile } from "app/features/Profile/Profile"; +import { PROFILE_SUMMARY, Profile } from "app/features/Profile/Profile"; import { setupIntersectionObserverMock, testCategoriesIsRendered, @@ -11,21 +11,13 @@ describe("test Profile", () => { setupIntersectionObserverMock(); render(); - expect(screen.getByText("Experienced business")).toBeInTheDocument(); - expect(screen.getByText("IT")).toBeInTheDocument(); - expect( - screen.getByText( - "professional described as, responsible, cooperative quick-learning, motivated and possessing a diverse background. Has experience leading teams, developing information systems and software using various forms of the", - ), - ).toBeInTheDocument(); - expect(screen.getAllByText("SDLC")).toHaveLength(2); - expect( - screen.getByText( - "and creating data reports. Experiences include administration, supervising, quality assurance software testing, project management, business systems analysis, creating websites, organizing massive amounts of online content to create user friendly", - ), - ).toBeInTheDocument(); - expect(screen.getByText("GUI")).toBeInTheDocument(); - expect(screen.getByText(", and user support.")).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[0].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[1].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[2].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[3].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[4].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[5].trim())).toBeInTheDocument(); + expect(screen.getByText(PROFILE_SUMMARY[6].trim())).toBeInTheDocument(); }); test("renders Categories", () => { diff --git a/app/features/Profile/Profile.tsx b/app/features/Profile/Profile.tsx index 994b14e..0dddb10 100644 --- a/app/features/Profile/Profile.tsx +++ b/app/features/Profile/Profile.tsx @@ -4,36 +4,40 @@ import { Zoom } from "react-awesome-reveal"; import { SKILLS } from "app/data/skills"; import { Categories } from "app/features/Profile/components/Categories"; +export const PROFILE_SUMMARY = [ + "Results-oriented business ", + "IT", + " professional recognized for accountability, collaboration, rapid adaptability, and unwavering motivation. Boasts a versatile background and a fervent dedication to web applications. Proven track record in steering teams, orchestrating the complete ", + "SDLC,", + " and crafting insightful data reports. Expertise spans administration, supervision, quality assurance, software testing, project management, business systems analysis, website and web application development. Known for adeptly organizing extensive online content to deliver intuitive ", + "GUI", + " experiences and providing exceptional user support.", +]; + export function Profile() { return ( <> - {"Experienced business "} + {PROFILE_SUMMARY[0]} - IT + {PROFILE_SUMMARY[1]} - - {` professional described as, responsible, cooperative quick-learning, motivated and possessing a diverse background. Has experience leading teams, developing information systems and software using various forms of the `} - + {PROFILE_SUMMARY[2]} - SDLC + {PROFILE_SUMMARY[3]} - - {` and creating data reports. Experiences include administration, supervising, quality - assurance software testing, project management, business systems analysis, creating - websites, organizing massive amounts of online content to create user friendly `} - + {PROFILE_SUMMARY[4]} - GUI + {PROFILE_SUMMARY[5]} - , and user support. + {PROFILE_SUMMARY[6]} diff --git a/app/images/recipes.jpg b/app/images/recipes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d2c1e6b21b6f85db8c6fe7cbfbbebdb883e2aab GIT binary patch literal 224341 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

&rug zCJr(Rbp56klf{}*w_g6Ns_mF?9kj$qO_ldIM&te`Mz3Npy7ylg-6HElfE14D48~xz z4oZU?gqRV8IH`T!^@0D%BO}ZMD2x6ti2}nlJL}y4#Wmor0Dne|xoiIL1_Wu9^6y*) znyaAt#}%{~tc#b7h=8B};U>i^f4!XB&_5V0_lG8mOh|K(q$?O_ri1?h*ngu51coLA z{-KHUJG)M8F*x@AifBC=;NzyZ3M&&ZxrR5O0qqZ+pWON*8BO~l2GD>;qZ5oa`4}Qa zA>?ze|HZ6!g*5!#KMkuob(B$U7|^Fr_BZNu^PlQ7UBt|WYyYsoIA~q_1LSN2lWzXF z|9-tje0hUVetG>z@{koUJRNv>i%1TcjX7I`7hm+oZX@QIn6aD{oaeIT+9aWX8>- z=WxdM5%B(cIg7Aa7ml=jPM7{$;hvnB)&~8S%zK_ML9a~XG9%a4;Cg7ldpJ;-d8W*y zfdP{ol@}SGuYKB^!WzIffBY&AL};aRBM}%QuHKLNx9iqJaECPN8Spv-a+-w6pwa^=t!~+ITXLi>9m)Nq7@>JcQ zG~eT%W;iYSt!Hf~UyUEk#n1N^WV=srF9-tG;#X7wI1%4gVQ*tT|RW{U%Uz%J%Ut>n~`b))M>egl){3`#g*wD8po0TaDYJRppFG zgsII<0p{*xpBAn&xb<|RHl`1GXP1f4C>U_a(;wO~l&eut)coo!m6JmoBY3z%Jr1dR z?HnSDC$e!85J1qlcN4;F;igzWioMbGM&9#)>V>5+wqp$ztPN4;s6JFQnOo$j5);mN%?)?6$?UaWR z-$AfPQU9d;gVt?PEVgPP(JjwRnCerJn)eM);%bG!VmIQKws6!}r;UsN_U_r({cOsa zv3Wsd(G=H_C(fQ%Iwq~r`FuRu?|$5St_(j|XUMtVE@+oc+MKTVebVt##+}g1aG{Y% zM&xZb0Ux>iVoTI-2AVl0^)%I*B&vgz==L&5rCY<7YF}MNxq8wDRihihW(YKzQ(&0& z;=8@jb36S9HK^awtQ8h^AVF<|FQzLUe8PP6q|3bucsdf+j^34B*>;Qi`~EBW*b}mn zoQb}g@k}^FDzvQOxb=epY`PAbg7TDQWaZ6D?@g*}HLVZk)XmD38nqNc2 zn)co~p3iz4xBD42Wygz;epI~;)a)k8O{bdmzMenw-!+Yom0C_GE3Im>!WaVCjrqKM5*nN6^WUd#o`$HKGPwd5~XEcEz3yp@gVkb@7hy*^CS8| z%?s9E?d|%{z9ygTkDoc55keLyN!z}8vvsa&*P+I3dcu?ZFF&#?Zf!cUJOxJYxHC8Jnnf4-}l-CMwp0Y$2`>gbR))1>$jZEpk4WF1ZJ%SS0b z?0lc3G5zfEfquGhn0Xr5d$8>H3rbLuo0w-ArL!Jso2qyD0gyO#MjH