xBlog

BLOG

Case study: Recreating LinkedIn work experiences timeline

Case study: Recreating LinkedIn work experiences timeline
Hamza BENKHALDOUN

Hamza BENKHALDOUN

09 September, 2021 Β· 4min πŸ“–

Hello everyone. In this blog post I will share with you how I was able to recreate LinkedIn work experiences timeline for one of our internal projects.

The technologies we will use for this demo will be ReactJs, Ant Design UI library and a little bit of Typescript to help us with some type safety and preventing bugs early.

Let's generate our project with this command:

yarn create react-app linkedin-timeline --template typescript

We should then go into the directory where we generated our project and add the ant design library with this command:

yarn add antd

Once the library is installed we will clear our template a little bit. We will delete the css files: App.css and index.css. Let's also delete the test file: App.test.tsx and the logo.svg file.

Once that is done let's adjust the index.tsx by deleting the third line

import './index.css';

Let's also clear the App.tsx file and replace its content with the following

function App() {
  return <div>It is working</div>;
}

export default App;

To test that everything is working fine run this command inside the folder that contains our project

yarn start

And then open our browser on this URL: http://localhost:3000/
You should see a page with the sentence "It is working" on the top left corner.

Now that we know that our project is bootstrapped correctly we can start working on our timeline.

Let's first define what a work experience is, in a file called workexperience.ts. Put the following content in that file:

export interface WorkExperience {
  company: string;
  startDate: Date;
  endDate: Date;
  title: string;
  description: string;
}

Now the behavior of our timeline should be like this:
As long as I am working in the same company, all my work experiences should be grouped under the same timeline. Once I leave that company, a new time line should start.

Our array of work experiences to test that our timeline is correct will be the following:

export const workexperiences: WorkExperience[] = [
  {
    company: "same",
    startDate: new Date("2020-01-01"),
    endDate: new Date("2020-12-31"),
    title: "Frontend developer",
    description: "Lorem Ipsum",
  },
  {
    company: "same",
    startDate: new Date("2019-01-01"),
    endDate: new Date("2019-04-01"),
    title: "Frontend intern",
    description: "Lorem Ipsum",
  },
  {
    company: "different",
    startDate: new Date("2019-04-02"),
    endDate: new Date("2019-09-02"),
    title: "Frontend intern plus",
    description: "Lorem Ipsum",
  },
  {
    company: "same",
    startDate: new Date("2021-01-01"),
    endDate: new Date("2021-12-31"),
    title: "Full Stack developer",
    description: "Lorem Ipsum",
  },
];

we should add this array to the end of the file workexperience.ts .

As you may have noticed, these work experiences are not sorted. We should sort them by last work experience chronologically should be first in the array.
To do that we will use the slice method ( to keep the original array intact ) and then the sort method to sort the array like this:

export const sortedWorkExperiences = workexperiences.slice()
  .sort((a, b) => {
  if (a.endDate > b.endDate) {
    return -1;
  } else if (a.endDate < b.endDate) {
    return 1;
  } else {
    return 0;
  }
});

Now that our experiences are sorted we need to group them by the company attribute.
The type of the grouped work experiences should be the following

export interface GroupedExperiences {
  company: string;
  startDate: Date;
  endDate: Date;
  experiences: WorkExperience[];
}

Now let's make group our work experiences using the sorted work experiences array that we created earlier using the reduce method like so


export const groupedWorkExperiences: GroupedExperiences[] =
  sortedWorkExperiences.reduce((acc: GroupedExperiences[], curr) => {
  
    // At the first loop through the array of sorted experiences,
    // by default we will have the groupedWorkExperiences containing
    // simply the data of the first element in the array
    
    if (acc.length === 0) {
      return [
        {
          company: curr.company,
          startDate: curr.startDate,
          endDate: curr.endDate,
          experiences: [curr],
        },
      ];
    }
    
    // Here we need to compare the company of the last work experience
    // with the company of the current element being looped over
    
    const prevGroupedExperience = acc[acc.length - 1];
    const prevCompany = prevGroupedExperience.company;
    
    // In case we are still at the same company
    
    if (prevCompany === curr.company) {
      return [
      
        // we need to keep the previous grouped work experiences
        // under other companies untouched, so we return a copy of
        // the grouped work experiences so far, except the last element
        // that we will need to change
        
        ...acc.slice(0, -1),
        
        // For the last element, we keep most of the data of the
        // prevGroupedExperience, we need to change the startDate,
        // and we need to add the current element to the array of
        // experiences
        
        {
          ...prevGroupedExperience,
          startDate: curr.startDate,
          experiences: [...prevGroupedExperience.experiences, curr],
        },
      ];
    }
    
    // In case we changed a company, we should then just add a new
    // entry to the array of groupedWorkExperiences, containing
    // data from the current element
    
    return [
      ...acc,
      {
        company: curr.company,
        startDate: curr.startDate,
        endDate: curr.endDate,
        experiences: [curr],
      },
    ];
  }, []);

Now that we have our grouped work experiences ready, all that is left to do is display them in the UI. But first let's define how display a single work experience. To do so we will create a file called SingleWorkExperience.tsx with the following content:

import { Typography } from "antd";
import { WorkExperience } from "./workexperience";

interface Props {
  workexperience: WorkExperience;
}

export const SingleWorkExperience = ({ workexperience }: Props) => {
  return (
    <>
      <Typography.Title level={3}>{workexperience.title}</Typography.Title>
      <div>
        <Typography.Text>
          {workexperience.startDate.toLocaleDateString("en-US")} -{" "}
          {workexperience.endDate.toLocaleDateString("en-US")}
        </Typography.Text>
      </div>
      <div>
        <Typography.Text type="secondary">
          {workexperience.description}
        </Typography.Text>
      </div>
    </>
  );
};

Now all that is left is to display our grouped work experiences in the App.tsx file. So let's replace its content with the following:

import React from "react";
import { Col, Row, Timeline, Typography } from "antd";
import { SingleWorkExperience } from "./SingleWorkExperience";
import { groupedWorkExperiences } from "./workexperience";
import "antd/dist/antd.css";

function App() {
  return (
    <div style={{ margin: "24px" }}>
      {groupedWorkExperiences.map((groupedXp) => (
        <React.Fragment>
          <Row gutter={24} align="middle">
            <Col>
              <Typography.Title level={1}>{groupedXp.company}</Typography.Title>
            </Col>
            <Col>
              <Typography.Text type="secondary">
                {groupedXp.startDate.toLocaleDateString("en-US")} -{" "}
                {groupedXp.endDate.toLocaleDateString("en-US")}
              </Typography.Text>
            </Col>
          </Row>
          <Timeline>
            {groupedXp.experiences.map((xp) => (
              <Timeline.Item>
                <SingleWorkExperience workexperience={xp} />
              </Timeline.Item>
            ))}
          </Timeline>
        </React.Fragment>
      ))}
    </div>
  );
}

export default App;

Now you should see something like this on your screen:

Final result

Finally, here is the repo containing the full source code: https://github.com/hamza0867/linkedin-timeline

Hamza BENKHALDOUN

Hamza BENKHALDOUN

I am a front-end developer, I love functional programming, I have experience with @reasonml, and some knowledge on @elmlang, @OCamlLang and Haskell

Tags:

signature

Hamza BENKHALDOUN has no other posts

Aloha from xHub team πŸ€™

We are glad you are here. Sharing is at the heart of our core beliefs as we like to spread the coding culture into the developer community, our blog will be your next IT info best friend, you will be finding logs about the latest IT trends tips and tricks, and more

Never miss a thing Subscribe for more content!

πŸ’Ό Offices

We’re remote friendly, with office locations around the world:
🌍 Casablanca, Agadir, Valencia, Quebec

πŸ“ž Contact Us:

🀳🏻 Follow us:

Β© XHUB. All rights reserved.

Made with πŸ’œ by xHub

Terms of Service