diff --git a/README-nisha.md b/README-nisha.md new file mode 100644 index 0000000..db24e1f --- /dev/null +++ b/README-nisha.md @@ -0,0 +1,24 @@ +# Thoughts while approaching this project + +## Understanding the existing code + +After installing and cloning this repository, I had a look at the existing code to work out what was already in place. I created a basic function to fetch the service result and log it to the console in order to play with it a little. + +After an initial investigation, I decided which files and components I wanted to create and how I wanted to handle the jobs data. I decided to handle the jobs object inside `QuestionOne` and to pass the array into a `JobsList` component. I would also pass a callback function that would call the service and request the jobs based on the search term. This callback would be passed to a `SearchField` component. + +Next, I created the `JobsList`. For the first iteration, all it did was accept a list of jobs and map them to a list with the necessary information. There was no styling or anything - that can be added later. + +The next step was to create the search box and functionality - the `SearchField`. I first set up the functional component with an input box that is controlled by a ref. I added a button and set it up to expect an onSearch callback, which will come from `QuestionOne`. I decided to use a button for now, just to make sure the link between the request and data display worked as expected. + +I then used the new components in `QuestionOne` and tested manually to make sure things were in place. When linking everything together, I noticed some issues with the `Pick` description of the service. As the service didn't filter the data, and we need to display the location plus use the ID, I adjusted the return type of the `IDataService` functions. + +Once satisfied with all of this, I next looked at the submit button. I removed it and the ref, and created a function called `onChange` which could be passed to the input. At this point, I ran the tests to ensure everything was okay. I adjusted the label of my input to match the tests, reran and everything passed! + +I then added some styling to the `JobsList` to make each job item clearer and a little prettier. I formatted the datetime to strings and separated them into date and time components. + +Future extensions could include + +* better styling +* animations when list appears/disappears +* placeholder where jobs list should be to indicate no jobs are available +* filters \ No newline at end of file diff --git a/src/common/types.ts b/src/common/types.ts index 7fbff30..5c09ade 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -8,8 +8,8 @@ export interface Job { } export interface IDataService { - getJobs: () => Promise[]>; + getJobs: () => Promise; getJobsWithSearchTerm: ( searchTerm: string - ) => Promise[]>; + ) => Promise; } diff --git a/src/components/JobsList.css b/src/components/JobsList.css new file mode 100644 index 0000000..870e42b --- /dev/null +++ b/src/components/JobsList.css @@ -0,0 +1,15 @@ +.job__item { + display: flex; + flex-direction: column; + border: 1px solid black; + padding: 1em; + border-radius: 25px; +} + +.title { + font-weight: bolder; +} + +.jobs { + list-style-type: none; +} \ No newline at end of file diff --git a/src/components/JobsList.tsx b/src/components/JobsList.tsx new file mode 100644 index 0000000..3df2b88 --- /dev/null +++ b/src/components/JobsList.tsx @@ -0,0 +1,31 @@ +import { FC } from "react"; +import { Job } from "../common/types"; +import './JobsList.css'; + +type Props = { + jobs: Job[]; +} + +const jobMapper = (job: Job) => { + return ( +
  • +
    {job.name}
    +
    Location: {job.location}
    +
    Start date: {new Date(job.start).toDateString()}
    +
    Start time: {new Date(job.start).toTimeString()}
    +
    End date: {new Date(job.end).toDateString()}
    +
    End time: {new Date(job.end).toTimeString()}
    +
  • + ) +} + +export const JobsList: FC = ({ jobs }) => { + return ( +
    + Here are the current jobs available based on your search: +
      + {jobs.map(jobMapper)} +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/SearchField.tsx b/src/components/SearchField.tsx new file mode 100644 index 0000000..da0a683 --- /dev/null +++ b/src/components/SearchField.tsx @@ -0,0 +1,30 @@ +import { ChangeEvent, FC, useCallback } from "react"; + +type Props = { + onSearch: (value: string) => void; + onClear: () => void; +} + +export const SearchField: FC = ({ onSearch, onClear }) => { + const onChange = useCallback((e: ChangeEvent) => { + if (e.target.value.length > 3) { + onSearch(e.target.value) + } + + if (e.target.value === '') { + onClear() + } + }, [onSearch, onClear]) + + + return ( +
    + Please type here to search for the jobs you would like to find: +
    + +
    +
    + ) +} \ No newline at end of file diff --git a/src/question-one/QuestionOne.tsx b/src/question-one/QuestionOne.tsx index fd12f51..9deb0d5 100644 --- a/src/question-one/QuestionOne.tsx +++ b/src/question-one/QuestionOne.tsx @@ -1,5 +1,7 @@ -import React from 'react'; -import { IDataService } from "../common/types"; +import React, { useCallback, useState } from 'react'; +import { IDataService, Job } from "../common/types"; +import { JobsList } from '../components/JobsList'; +import { SearchField } from '../components/SearchField'; import { SectionGroup } from '../components/section/SectionGroup'; import { SectionPanel } from '../components/section/SectionPanel'; import './QuestionOne.css'; @@ -7,18 +9,26 @@ import './QuestionOne.css'; export const QuestionOne: React.FC<{ service: IDataService }> = ({ service, }) => { + const [jobs, setJobs] = useState([]); + + const searchForJobs = useCallback(async (searchTerm: string) => { + const data = await service.getJobsWithSearchTerm(searchTerm); + setJobs(data) + }, [service, setJobs]) + + const clearJobs = useCallback(() => { + setJobs([]); + }, [setJobs]) + return (
    - Please refer to src/INSTRUCTIONS.md -
    - {/* Render a search field here... */} +
    -
    - {/* Render a list of results here... */} +