# Build a Time Tracking Dashboard using Angular and Tailwind CSS

## Overview
In this tutorial I show you my step by step process to complete a challenge from Frontend Mentor. We will create a responsive Time Tracking Dashboard with Angular and Tailwind CSS. 

## Prerequisites
- Angular CLI
- Basic knowledge of Angular
- A free account from [frontendmentor.io](https://www.frontendmentor.io/challenges/time-tracking-dashboard-UIQ7167Jw) (in order to download the challenge assets)

## Workspace setup
### Creating a new workspace
Start by creating a new workspace with Angular CLI. Open a terminal in the directory you want to generate your project and enter the following command:
```bash
ng new time-tracking-dashboard
```
You then receive the following prompts in your command line:

```bash
Would you like to add Angular routing?:     n
Wich stylesheet format would you like to use?:     SCSS
```

After that, needed packages will be dowloaded and a folder time-tracking-dashboard will be created in your directory. Open that folder in your IDE - I use VSCode.

### Copying assets

Copy images folder and data.json file from the assets downloaded from [frontendmentor.io](https://www.frontendmentor.io/challenges/time-tracking-dashboard-UIQ7167Jw) and paste them in your Angular project assets directory, **src/assets**:


<figure>
  ![Figure 1: Assets folder](https://cdn.hashnode.com/res/hashnode/image/upload/v1662438371944/uzspi3Sho.png align="left")
  <figcaption>Figure 1: Assets folder</figcaption>
</figure>

### Installing Tailwind CSS

Inside your Angular project, run the following npm command:

```bash
npm install -D tailwindcss postcss autoprefixer
```

Then generate a **tailwind.config.js** file with the following command:

```bash
npx tailwindcss init
```
Inside the **tailwind.config.js** file, make sure the content looks like the following:

```jsx
/** @type {import('tailwindcss').Config} */ 
module.exports = {
  content: [
    "./src/**/*.{html,ts}", // <== update
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Now copy the following lines inside **src/styles.scss*:***

```jsx
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Let’s see if our Tailwind CSS setup works. Replace the content of **src/app/app.component.html** by the following:

```jsx
<h1 class="text-3xl text-blue-500 capitalize ">
  time tracking dashboard
</h1>
```

Then use the following command to start your Angular project and open it in your browser:

```bash
ng serve --o
```

You should have the following screen:

<figure>
  ![Figure 2: Tailwind CSS setup test](https://cdn.hashnode.com/res/hashnode/image/upload/v1662439622787/PuV5YXkiH.png align="left")
<figcaption>Figure 2: Tailwind CSS setup test</figcaption>
</figure>

### Setting up fonts

Let’s go to [Google Fonts](https://fonts.google.com/)  first to get fonts we need.
Then type **Rubik** in the search bar and choose the first font. 

<figure>
  ![Figure 3: Rubik font search](https://cdn.hashnode.com/res/hashnode/image/upload/v1662439817567/rpiJ6kiEN.png align="left")
<figcaption>Figure 3: Rubik font search</figcaption>
</figure>


On the Rubik styles page, select the following:

- Light 300
- Regular 400
- Medium 500

Then click on the last element on right in the navigation bar to display a new menu. When the lateral menu opens, choose **@import ** then copy the content between **style** tag.
<figure>
![Figure 4: Rubik styles page](https://cdn.hashnode.com/res/hashnode/image/upload/v1662440915864/2T-Wfm79y.png align="left")
  <figcaption>Figure 4: Rubik styles page</figcaption>
</figure>

Now get back to the Angular project in **src/styles.scss** file and paste the code you copied from Google Fonts. That file will now look like the following:

```jsx
/* You can add global styles to this file, and also import other style files */
@tailwind base;
@tailwind components;
@tailwind utilities;

// update
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500&display=swap');
```

Next, go to **tailwind.config.js** file at the root of the project to configure the default font that will be used.  The content of that file should look like the following:
```jsx
/** @type {import('tailwindcss').Config} */

module.exports = {
  content: ["./src/**/*.{html,ts}"],
  theme: {
    extend: {
      fontFamily: { // <== update
        "sans": ["Rubik", "sans-serif"],
      },
    },
  },
  plugins: [],
};
```

We are now able to use **font-light**, **font-regular** or **font-medium** class to give light, regular or medium weight to the texts.

Let’s also configure the font sizes we are going to use. The **tailwind.config.js** file should now look like the following:

```jsx
/** @type {import('tailwindcss').Config} */

module.exports = {
  content: ["./src/**/*.{html,ts}"],
  theme: {
    extend: {
      fontFamily: {
        "sans": ["Rubik", "sans-serif"],
      },
      fontSize: { // <== update
        "tiny": "15px",
        "base": "18px",
        "lg": "24px",
        "xl": "32px",
        "2xl": "40px",
        "3xl": "56px",
      },
    },
  },
  plugins: [],
};
```

### Setting up color palette

Let’s define the color palette in the **tailwind.config.js** file. The file should look like the following now:

```jsx
/** @type {import('tailwindcss').Config} */

module.exports = {
  content: ["./src/**/*.{html,ts}"],
  theme: {
    extend: {
      fontFamily: {
        "sans": ["Rubik", "sans-serif"],
      },
      fontSize: {
        "tiny": "15px",
        "base": "18px",
        "lg": "24px",
        "xl": "32px",
        "2xl": "40px",
        "3xl": "56px",
      },
      colors: { // <== update
        "blue": "#5847EB",
        "desaturated-red": "#FF8C66",
        "soft-blue": "#56C2E6",
        "light-red": "#FF5C7C",
        "lime-green": "#4ACF81",
        "violet": "#7536D3",
        "soft-orange": "#F1C65B",
        "very-dark-blue": "#0F1424",
        "dark-blue": "#1C1F4A",
        "desaturated-blue": "#6F76C8",
        "pale-blue": "#BDC1FF",
		"hover-blue": "#34397B"
      }
    },
  },
  plugins: [],
};
```
### Setting up spacings

Let’s define the spacing values in the **tailwind.config.js** file. The file should look like the following now:

```jsx
/** @type {import('tailwindcss').Config} */

module.exports = {
  content: ["./src/**/*.{html,ts}"],
  theme: {
    extend: {
      fontFamily: {
        "sans": ["Rubik", "sans-serif"],
      },
      fontSize: {
        "tiny": "15px",
        "base": "18px",
        "lg": "24px",
        "xl": "32px",
        "2xl": "40px",
        "3xl": "56px",
      },
      colors: {
        "blue": "#5847EB",
        "desaturated-red": "#FF8C66",
        "soft-blue": "#56C2E6",
        "Light-red": "#FF5C7C",
        "lime-green": "#4ACF81",
        "violet": "#7536D3",
        "soft-orange": "#F1C65B",
        "very-dark-blue": "#0F1424",
        "dark-blue": "#1C1F4A",
        "desaturated-blue": "#6F76C8",
        "pale-blue": "#BDC1FF",
				"hover-blue": "#34397B"
      },
      spacing: { // <== update
        "1": "8px",
        "2": "18px",
        "3": "24px",
        "4": "28px",
        "5": "32px",
        "6": "38px",
        "7": "40px",
        "8": "68px",
        "9": "74px",
        "10": "80px",
        "11": "256px",
        "12": "324px"
      }
    },
  },
  plugins: [],
};
```

## Mobile design implementation

<figure>
  ![Figure 5: Time Tracking Dashboard mobile design](https://cdn.hashnode.com/res/hashnode/image/upload/v1662441746290/xKCcAJwXGn.png align="left")
<figcaption>Figure 5: Time Tracking Dashboard mobile design</figcaption>
</figure>

The image above represents the mobile interface. It can be broken in three parts as shown in the image below:

- A main container (the blue arrow)
- A report component (the red arrow)
- Multiple activity components (yellow arrows)

<figure>
  ![Figure 6: Mobile design decomposition](https://cdn.hashnode.com/res/hashnode/image/upload/v1662441938484/mtcLx433p.png align="left")
<figcaption>Figure 6: Mobile design decomposition</figcaption>
</figure>

### Main container

Let’s start with the Main container. First, open **src/index.html** and update **body** as the following:

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>TimeTrackingDashboard</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body class="grid items-center justify-center min-h-screen text-base bg-very-dark-blue text-pale-blue">
  <app-root></app-root>
</body>
</html>
```

Then open **src/app/app.component.html** file and replace its content by the following:

```html
<!-- main container -->
<main class="grid grid-cols-1 gap-3 px-3 py-10">

</main>
```

The following image shows the result we get in the browser when we preview the mobile version of the app:

<figure>
![Figure 7: Main container preview](https://cdn.hashnode.com/res/hashnode/image/upload/v1662442403313/7_KxV8wLp.png align="left")
<figcaption>Figure 7: Main container preview</figcaption>
</figure>

### Report component

<figure>
![Figure 8: Report component](https://cdn.hashnode.com/res/hashnode/image/upload/v1662443040269/holC_zc2U.png align="left")
<figcaption>Figure 8: Report component</figcaption>
</figure>

Now, using the Angular CLI generate the report component with the following command:

```bash
ng generate component report
```

The previous command will create all necessary component files inside the **src/app/report** folder:

<figure>
![Figure 9: Report component folder](https://cdn.hashnode.com/res/hashnode/image/upload/v1662443168976/1a14TtOB9.png align="left")
<figcaption>Figure 9: Report component folder</figcaption>
</figure>

Now let’s import that component in the main container. This is done by adding **app-report** tag inside the **src/app/app.component.html** file. This file should now look like the following:

```html
<!-- main container -->
<main class="grid grid-cols-1 gap-3 px-3 py-10">

	<!-- report component -->
	<app-report></app-report>
</main>
```

Next let’s break down the report component into several blocks. The following image shows the decomposition of the report component:

<figure>
![Figure 10: Report component decomposition](https://cdn.hashnode.com/res/hashnode/image/upload/v1662443776826/jXDh96jK4.png align="left")
<figcaption>Figure 10: Report component decomposition</figcaption>
</figure>

#### Report and Report Nav blocks

In the **src/app/report/report.component.ts** file add two variables:
- **periods:** It contains an array of periods
- **activePeriod:** It indicates the selected period

Also add a method to change **activePeriod** variable each time a period is selected. This method will be called **setActivePeriod**. The **src/app/report/report.component.ts** should look like the following:

```typescript
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-report',
  templateUrl: './report.component.html',
  styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {
  
  // update
  periods: string[] = ["Daily", "Weekly", "Monthly"];
  activePeriod: string = "Daily";

  constructor() { }

  ngOnInit(): void {
  }

  // update
  setActivePeriod(period: string): void {
    this.activePeriod = period;
  }

}
```

Then open the **src/app/report/report.component.html** file and replace its content with the following:

```html
<!-- Report -->
<div class="bg-dark-blue rounded-2xl">

  <!-- Report Nav -->
  <div class="flex justify-between px-4 py-3 text-desaturated-blue">
    <span
      *ngFor="let period of periods"
      (click)="setActivePeriod(period)"
      [ngClass]="{'text-white': activePeriod === period}"
    >{{ period }}</span>
  </div>
</div>
```

If we preview the application now it should look like the following:

<figure>
![Figure 11: Report component - Report and nav blocks](https://cdn.hashnode.com/res/hashnode/image/upload/v1662444343141/XQt30lfyt.png align="left")
<figcaption>Figure 11: Report component - Report and nav blocks</figcaption>
</figure>

Update **src/app/report/report.component.html** file content to be the same as the following:

```html
<!-- Report -->
<div class="w-12 bg-dark-blue rounded-2xl">

  <!-- Report Header -->
  <div class="flex items-center px-4 bg-blue py-5 rounded-2xl">

    <!-- Report Image -->
    <div class="border-2 border-white rounded-full w-9 mr-2">
      <img src="/assets/images/image-jeremy.png" alt="user profile picture" />
    </div>

    <!-- Report Title -->
    <h1 class="text-tiny">
      Report for <br>
      <span class="text-lg font-light text-white">
        Jeremy Robson
      </span>
    </h1>
  </div>

  <!-- Report Nav -->
  <div class="flex justify-between px-4 py-3 text-desaturated-blue">
    <span
      *ngFor="let period of periods"
      (click)="setActivePeriod(period)"
      [ngClass]="{'text-white': activePeriod === period}"
    >{{ period }}</span>
  </div>
</div>
```

Previewing the application should now look like the following:

<figure>
  ![Figure 12: Report component - header, image and title blocks](https://cdn.hashnode.com/res/hashnode/image/upload/v1662444539154/IBlvj-IGH.png align="left")
  <figcaption>Figure 12: Report component - header, image and title blocks</figcaption>
</figure>

### Activity components

<figure>
![Figure 13: Activity component](https://cdn.hashnode.com/res/hashnode/image/upload/v1662446179037/AwV5R2Qi-.png align="left")
<figcaption>Figure 13: Activity component</figcaption>
</figure>

Generate the activity component with the following command:

```bash
ng generate component activity
```
The previous command will create all necessary component files inside the **src/app/activity** folder:

<figure>
![Figure 14: Activity component folder](https://cdn.hashnode.com/res/hashnode/image/upload/v1662446355408/U_QILRBl9.png align="left")
<figcaption>Figure 14: Activity component folder</figcaption>
</figure>

A JSON file that contains the activities is located in **src/assets/data** folder. We will create a model that follows the architecture of an activity object and then we will generate a service to call these activities.

In **src/app/activity** folder, create a new file named **activity.model.ts** and make sure its content is the same as the following:

```typescript
export interface IActivity {
  title: string;
  timeframes: ITimeframe;
}

export interface ITimeframe {
  daily: ITimeframeDetail;
  weekly: ITimeframeDetail;
  monthly: ITimeframeDetail;
}

export interface ITimeframeDetail {
  current: number;
  previous: number;
}
```

Next, create inside **src/app/activity** folder, a new file named **mock-activities.ts** . The following image shows the content of **src/app/activity** folder:

<figure>
![Figure 15: Activity component folder - mock-activities](https://cdn.hashnode.com/res/hashnode/image/upload/v1662446545612/GQN8e-Jo9.png align="left")
<figcaption>Figure 15: Activity component folder - mock-activities</figcaption>
</figure>

Then copy the content of **src/assets/data/data.json** in **src/app/activity/mock-activities.ts**  file and declare a variable to export it. The content of **src/app/activity/mock-activities.ts** should now look like the following:

```typescript
import { IActivity } from './activity.model';

export const ACTIVITIES: IActivity[] = [
  {
    "title": "Work",
    "timeframes": {
      "daily": {
        "current": 5,
        "previous": 7
      },
      "weekly": {
        "current": 32,
        "previous": 36
      },
      "monthly": {
        "current": 103,
        "previous": 128
      }
    }
  },
  {
    "title": "Play",
    "timeframes": {
      "daily": {
        "current": 1,
        "previous": 2
      },
      "weekly": {
        "current": 10,
        "previous": 8
      },
      "monthly": {
        "current": 23,
        "previous": 29
      }
    }
  },
  {
    "title": "Study",
    "timeframes": {
      "daily": {
        "current": 0,
        "previous": 1
      },
      "weekly": {
        "current": 4,
        "previous": 7
      },
      "monthly": {
        "current": 13,
        "previous": 19
      }
    }
  },
  {
    "title": "Exercise",
    "timeframes": {
      "daily": {
        "current": 1,
        "previous": 1
      },
      "weekly": {
        "current": 4,
        "previous": 5
      },
      "monthly": {
        "current": 11,
        "previous": 18
      }
    }
  },
  {
    "title": "Social",
    "timeframes": {
      "daily": {
        "current": 1,
        "previous": 3
      },
      "weekly": {
        "current": 5,
        "previous": 10
      },
      "monthly": {
        "current": 21,
        "previous": 23
      }
    }
  },
  {
    "title": "Self Care",
    "timeframes": {
      "daily": {
        "current": 0,
        "previous": 1
      },
      "weekly": {
        "current": 2,
        "previous": 2
      },
      "monthly": {
        "current": 7,
        "previous": 11
      }
    }
  }
]
```

Now generate a service with the following command:

```bash
ng generate service activity/activity
```

The previous command will generate two new files in the activity folder: 

- **activity.service.ts**
- **activity.service.spec.ts**

Open **src/app/activity/activity.service.ts** file and update its content to be the same as the following:

```typescript
import { IActivity } from './activity.model';
import { Injectable } from '@angular/core';
import { ACTIVITIES } from './mock-activities';

@Injectable({
  providedIn: 'root'
})
export class ActivityService {

  constructor() { }

  // update
  getActivities(): IActivity[] {
    return ACTIVITIES;
  }
}
```

Now open **src/app/app.component.ts** file and update its content to be the same as the following:

```typescript
import { ActivityService } from './activity/activity.service';
import { IActivity } from './activity/activity.model';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  activities: IActivity[] = []; // update

  // update
  constructor(private activityService: ActivityService) {}
  
  // update
  ngOnInit(): void {
    this.activities = this.activityService.getActivities();
  }

}
```

Next, let’s build the view for just one activity component. To do that we will create a new variable named **sampleActivity** in **src/app/app.component.ts** and set the first element of the list of activities as the value of that variable.

After that add **app-activity** tag in **src/app/app.component.html**. That tag will be used to display the **sampleActivity**. The files **src/app/app.component.ts** and **src/app/app.component.html** should look like the following:

```typescript
import { ActivityService } from './activity/activity.service';
import { IActivity } from './activity/activity.model';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  activities: IActivity[] = [];
  sampleActivity!: IActivity; // update

  constructor(private activityService: ActivityService) {}

  ngOnInit(): void {
    this.activities = this.activityService.getActivities();
    this.sampleActivity = this.activities[0]; // update
  }

}
```

```html
<!-- main container -->
<main
  class="grid grid-cols-1 gap-3 px-3 py-10">
  <!-- report component -->
  <app-report></app-report>

  <!-- activity component -->
  <app-activity></app-activity>
</main>
```

Previewing the application should now look like the following:

<figure>
![Figure 16: Activity component import](https://cdn.hashnode.com/res/hashnode/image/upload/v1662452411934/Zbxr-U-bm.png align="left")
<figcaption>Figure 16: Activity component import</figcaption>
</figure>

Next let’s break down the activity component into several blocks. The following image shows the decomposition of the activity component:

<figure>
![Figure 17: Activity component decomposition](https://cdn.hashnode.com/res/hashnode/image/upload/v1662453315263/gFv8Z-50A.png align="left")
<figcaption>Figure 17: Activity component decomposition</figcaption>
</figure>

Each activity component will receive data from the parent component. We will use the **property binding** concept to achieve that. 

Open **src/app/activity/activity.component.ts** file and update its content to look like the following:

```typescript
import { IActivity } from './activity.model';
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-activity',
  templateUrl: './activity.component.html',
  styleUrls: ['./activity.component.scss']
})
export class ActivityComponent implements OnInit {
  
  // update
  @Input() activity!: IActivity;

  constructor() { }

  ngOnInit(): void {
  }

}
```

Now we can get an activity object from the parent. *src/app/app.component.html* should look like the following:

```html
<!-- main container -->
<main
  class="grid grid-cols-1 gap-3 px-3 py-10">
  <!-- report component -->
  <app-report></app-report>

  <!-- activity component -->
  <app-activity [activity]="sampleActivity"></app-activity>
</main>
```
Update **src/app/activity/activity.component.html** to display the title of the activity received via property binding from the parent. **src/app/activity/activity.component.html** should look like the following:

```html
<div>{{activity.title}}</div>
```

The following shows the application preview:

<figure>
![Figure 18: Activity component- Property binding test](https://cdn.hashnode.com/res/hashnode/image/upload/v1662619568615/DMzf9Rzgx.png align="left")
<figcaption>Figure 18: Activity component- Property binding test</figcaption>
</figure>

Each activity component has a specific color and image so, update **src/app/activity/activity.service.ts** by adding two methods: one to get the image and another to get the color.
**src/app/activity/activity.service.ts** should look now like the following:

```typescript
import { IActivity } from './activity.model';
import { Injectable } from '@angular/core';
import { ACTIVITIES } from './mock-activities';

@Injectable({
  providedIn: 'root'
})
export class ActivityService {

  constructor() { }

  getActivities(): IActivity[] {
    return ACTIVITIES;
  }

  // update
  getImage(title: string): string {
    let url = "";

    switch(title) {
      case "Work":
        url = "/assets/images/icon-work.svg";
      break;
      case "Play":
        url = "/assets/images/icon-play.svg";
      break;
      case "Study":
        url = "/assets/images/icon-study.svg";
      break;
      case "Exercise":
        url = "/assets/images/icon-exercise.svg";
      break;
      case "Social":
        url = "/assets/images/icon-social.svg";
      break;
      case "Self Care":
        url = "/assets/images/icon-self-care.svg";
      break;
    }

    return url;
  }

  // update
  getColor(title: string): string {
    let color = "";

    switch(title) {
      case "Work":
        color = "bg-desaturated-red";
      break;
      case "Play":
        color = "bg-soft-blue";
      break;
      case "Study":
        color = "bg-light-red";
      break;
      case "Exercise":
        color = "bg-lime-green";
      break;
      case "Social":
        color = "bg-violet";
      break;
      case "Self Care":
        color = "bg-soft-orange";
      break;
    }

    return color;
  }

}
```

Now add methods in **src/app/activity/activity.component.ts** file that will call previously created methods. **src/app/activity/activity.component.ts** file content should look like the following:

```typescript
import { ActivityService } from './activity.service';
import { IActivity } from './activity.model';
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-activity',
  templateUrl: './activity.component.html',
  styleUrls: ['./activity.component.scss']
})
export class ActivityComponent implements OnInit {

  @Input() activity!: IActivity;

  // update
  constructor(private activityService: ActivityService) { }

  ngOnInit(): void {
  }
  
  // update
  getColor(title: string): string {
    return this.activityService.getColor(title);
  }

  // update
  getImage(title: string): string {
    return this.activityService.getImage(title);
  }

}
```

#### Activity and Activity Header blocks

Update **src/app/activity/activity.component.html** file content to be the same as the following:
```html
<!-- Activity -->
<div [ngClass]="[getColor(activity.title), 'rounded-2xl', 'w-12']">

  <!-- Activity Header -->
  <div class="relative h-6 overflow-hidden">
    <img class="absolute -mt-1 right-3 w-8" [src]="getImage(activity.title)" [alt]="activity.title" />
  </div>
</div>
```

If we preview our application, we will have the following:

<figure>
![Figure 19: Activity component - Activity and header blocks](https://cdn.hashnode.com/res/hashnode/image/upload/v1662620192894/MAz33X456.png align="left")
<figcaption>Figure 19: Activity component - Activity and header blocks</figcaption>
</figure>

#### Activity body, Activity Title  and Activity Time blocks

Update **src/app/activity/activity.component.html** file content to be the same as the following:

```html
<!-- Activity -->
<div [ngClass]="[getColor(activity.title), 'rounded-2xl', 'w-12']">

  <!-- Activity Header -->
  <div class="relative h-6 overflow-hidden">
    <img class="absolute -mt-1 right-3 w-8" [src]="getImage(activity.title)" [alt]="activity.title" />
  </div>

  <!-- Activity Body -->
  <div class="px-3 py-4 bg-dark-blue rounded-2xl">

    <!-- Activity Title -->
    <div class="flex items-center justify-between">
      <h2 class="font-medium text-white">{{activity.title}}</h2>
      <div>
        <img src="/assets/images/icon-ellipsis.svg" alt="more" />
      </div>
    </div>

    <!-- Activity Time -->
    <div class="flex items-center justify-between">
      <span class="text-xl font-light text-white">{{activity.timeframes.weekly.current}}hrs</span>
      <span class="text-tiny">Last week - {{activity.timeframes.weekly.previous}}hrs</span>
    </div>
  </div>
</div>
```

Previewing the application should look like the following:

<figure>
![Figure 20: Activity component - body, header and title blocks](https://cdn.hashnode.com/res/hashnode/image/upload/v1662621248300/RkFoITavr.png align="left")
<figcaption>Figure 20: Activity component - body, header and title blocks</figcaption>
</figure>

Now let’s synchronize the timeframe selected in **report** component with the **activity** components. To make the synchronization work, we will add a method in the report component that will send the selected timeframe to the parent component (the app component), then from the app component we will pass that timeframe to the activity component.

Open **src/app/report/report.component.ts** file and update its content to look like the following:

```typescript
import { Component, EventEmitter, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-report',
  templateUrl: './report.component.html',
  styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {

  // update
  @Output() changePeriod = new EventEmitter();

  periods: string[] = ["Daily", "Weekly", "Monthly"];
  activePeriod: string = "Daily";

  constructor() { }

  ngOnInit(): void {
  }

  setActivePeriod(period: string): void {
    this.activePeriod = period;

    // update
    this.changePeriod.emit(this.activePeriod);
  }

}
```

Open **src/app/app.component.ts** file and update its content to look like the following:

```typescript
import { ActivityService } from './activity/activity.service';
import { IActivity } from './activity/activity.model';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  activities: IActivity[] = [];
  sampleActivity!: IActivity;
  activePeriod = "Daily";  // update

  constructor(private activityService: ActivityService) {}

  ngOnInit(): void {
    this.activities = this.activityService.getActivities();
    this.sampleActivity = this.activities[0];
  }

  // update
  setActivePeriod(period: string): void {
    this.activePeriod = period;
  }

}
```

Open **src/app/activity/activity.component.ts** and update its content to look like the following:

```typescript
import { ActivityService } from './activity.service';
import { IActivity } from './activity.model';
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-activity',
  templateUrl: './activity.component.html',
  styleUrls: ['./activity.component.scss']
})
export class ActivityComponent implements OnInit {

  @Input() activity!: IActivity;
  @Input() period!: string; // update

  current: string = ""; // update
  previous: string = ""; // update

  constructor(private activityService: ActivityService) { }

  ngOnInit(): void {
  }

  // update
  ngOnChanges() {
    if(this.period === "Daily") {
      this.current = this.activity.timeframes.daily.current + "hrs";
      this.previous = "Yesterday - " + this.activity.timeframes.daily.previous + "hrs";
    }

     if(this.period === "Weekly") {
      this.current = this.activity.timeframes.weekly.current + "hrs";
      this.previous = "Last week - " + this.activity.timeframes.weekly.previous + "hrs";
    }

    if(this.period === "Monthly") {
      this.current = this.activity.timeframes.monthly.current + "hrs";
      this.previous = "Last month - " + this.activity.timeframes.monthly.previous + "hrs";
    }
  }

  getColor(title: string) {
    return this.activityService.getColor(title);
  }

  getImage(title: string): string {
    return this.activityService.getImage(title);
  }

}
```
Open **src/app/activity/activity.component.html** and update its content to look like the following:

```html
<!-- Activity -->
<div [ngClass]="[getColor(activity.title), 'rounded-2xl', 'w-12']">

  <!-- Activity Header -->
  <div class="relative h-6 overflow-hidden">
    <img class="absolute -mt-1 right-3 w-8" [src]="getImage(activity.title)" [alt]="activity.title" />
  </div>

  <!-- Activity Body -->
  <div class="px-3 py-4 bg-dark-blue rounded-2xl">

    <!-- Activity Title -->
    <div class="flex items-center justify-between">
      <h2 class="font-medium text-white">{{activity.title}}</h2>
      <div>
        <img src="/assets/images/icon-ellipsis.svg" alt="more" />
      </div>
    </div>

    <!-- Activity Time -->
    <div class="flex items-center justify-between">
      <span class="text-xl font-light text-white">{{ current }}</span>
      <span class="text-tiny">{{ previous }}</span>
    </div>
  </div>
</div>
```

Finally open **src/app/app.component.html** and update its content to look like the following:

```html
<!-- main container -->
<main
  class="grid grid-cols-1 gap-3 px-3 py-10">
  <!-- report component -->
  <app-report (changePeriod)="setActivePeriod($event)" ></app-report>

  <!-- activity component -->
  <app-activity [activity]="sampleActivity" [period]="activePeriod"></app-activity>
</main>
```

Now each time you select a timeframe on the report component display, the hours of the activity also change.

Now that our application works well for one single activity component, we will remove the sample activity we created before and then use the array of activities to display all of them. 

Open **src/app/app.component.ts** and update its content to look like the following:
```typescript
import { ActivityService } from './activity/activity.service';
import { IActivity } from './activity/activity.model';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  activities: IActivity[] = [];
  activePeriod = "Daily";

  constructor(private activityService: ActivityService) {}

  ngOnInit(): void {
    this.activities = this.activityService.getActivities();
  }

  setActivePeriod(period: string): void {
    this.activePeriod = period;
  }

}
```

Open **src/app/app.component.html** and update its content to look like the following:

```html
<!-- main container -->
<main
  class="grid grid-cols-1 gap-3 px-3 py-10">
  <!-- report component -->
  <app-report (changePeriod)="setActivePeriod($event)" ></app-report>

  <!-- activity component -->
  <app-activity *ngFor="let activity of activities" [activity]="activity" [period]="activePeriod"></app-activity>
</main>
```

Previewing the application should look like the following:

<figure>
![Figure 21: Activity components list](https://cdn.hashnode.com/res/hashnode/image/upload/v1662622603499/Mwfgh7Y9F.png align="left")
<figcaption> Figure 21: Activity components list</figcaption>
</figure>

## Desktop Design Implementation

<figure>
![Figure 22: Time Tracking Dashboard desktop design](https://cdn.hashnode.com/res/hashnode/image/upload/v1662625182207/XcPVp8NW_.jpg align="left")
<figcaption>Figure 22: Time Tracking Dashboard desktop design</figcaption>
</figure>

On desktop, the view changes from single column to multiple rows and columns. The breakpoint used for screen max-width is 1280px.

Open **src/app/app.component.html** file and update its content to look like the following:
```html
<!-- main container -->
<main
  class="grid grid-cols-1 gap-3 px-3 py-10 xl:grid-cols-4 xl:grid-rows-2">
  <!-- report component -->
  <app-report class="xl:row-span-2" (changePeriod)="setActivePeriod($event)" ></app-report>

  <!-- activity component -->
  <app-activity *ngFor="let activity of activities" [activity]="activity" [period]="activePeriod"></app-activity>
</main>
```

If we preview the application on at list 1280px screen width, it should look like the following:

<figure>
![Figure 23: Preview on 1280px screen width](https://cdn.hashnode.com/res/hashnode/image/upload/v1662625375814/vei27NFDY.png align="left")
<figcaption>Figure 23: Preview on 1280px screen width</figcaption>
</figure>

Open **src/app/report/report.component.html** file and update its content to look like the following:

```html
<!-- Report -->
<div class="w-12 bg-dark-blue rounded-2xl xl:w-11 xl:h-full">

  <!-- Report Header -->
  <div class="flex items-center px-4 bg-blue py-5 rounded-2xl xl:flex-col xl:items-start xl:px-5 xl:h-2/3">

    <!-- Report Image -->
    <div class="mr-2 border-2 border-white rounded-full w-9 xl:mb-7">
      <img src="/assets/images/image-jeremy.png" alt="user profile picture" />
    </div>

    <!-- Report Title -->
    <h1 class="text-tiny">
      Report for <br>
      <span class="text-lg font-light text-white xl:text-2xl xl:leading-snug">
        Jeremy Robson
      </span>
    </h1>
  </div>

  <!-- Report Nav -->
  <div class="flex justify-between px-4 py-3 text-desaturated-blue xl:flex-col xl:px-5 xl:content-between xl:h-1/3 xl:w-fit">
    <span
      *ngFor="let period of periods"
      (click)="setActivePeriod(period)"
      [ngClass]="{
        'text-white': activePeriod === period,
        'xl:hover:text-white': true,
        'xl:cursor-pointer': true
      }"
    >{{ period }}</span>
  </div>
</div>
```

Then update **src/app/activity/activity.component.html** file content to look like the following: 

```html
<!-- Activity-->
<div [ngClass]="[getColor(activity.title), 'rounded-2xl', 'w-12', 'xl:w-11']">

  <!-- Activity Header -->
  <div class="relative h-6 overflow-hidden">
    <img class="absolute -mt-1 right-3 w-8" [src]="getImage(activity.title)" [alt]="activity.title" />
  </div>

  <!-- Activity Body -->
  <div class="px-3 py-4 bg-dark-blue rounded-2xl xl:p-5 xl:cursor-pointer xl:hover:bg-hover-blue">

    <!-- Activity Title -->
    <div class="flex items-center justify-between xl:mb-3">
      <h2 class="font-medium text-white">{{activity.title}}</h2>
      <div>
        <img src="/assets/images/icon-ellipsis.svg" alt="more" />
      </div>
    </div>

    <!-- Activity Time -->
    <div class="flex items-center justify-between xl:flex-col xl:items-start">
      <span class="text-xl font-light text-white xl:text-3xl">{{ current }}</span>
      <span class="text-tiny">{{ previous }}</span>
    </div>
  </div>
</div>
```

If we preview the application it should now look like the following:

<figure>
![Figure 24: Responsive Time Tracking Dashboard](https://cdn.hashnode.com/res/hashnode/image/upload/v1662625623792/NKfgnb_M5.png align="left")
<figcaption>Figure 24: Responsive Time Tracking Dashboard</figcaption>
</figure>

Now we have a responsive Time Tracking Dashboard built with Angular v13 and Tailwind CSS v3.

## Project source code
You can download the project source code from Github at [https://github.com/lioneltraore/frontendmentor.io-challenges/tree/main/Angular/time-tracking-dashboard](https://github.com/lioneltraore/frontendmentor.io-challenges/tree/main/Angular/time-tracking-dashboard)

## Live site
You can see the live site [here](https://sensational-chimera-8046b2.netlify.app/)
