├── preview.jpg
├── images
├── Ayokanmi.png
├── favicon-32x32.png
├── icon-ellipsis.svg
├── icon-self-care.svg
├── icon-work.svg
├── icon-play.svg
├── icon-study.svg
├── icon-social.svg
└── icon-exercise.svg
├── design
├── active-states.jpg
├── desktop-design.jpg
└── mobile-design.jpg
├── style-guide.md
├── index.html
├── data.json
├── README.md
├── styles.css
└── script.js
/preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/preview.jpg
--------------------------------------------------------------------------------
/images/Ayokanmi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/images/Ayokanmi.png
--------------------------------------------------------------------------------
/design/active-states.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/design/active-states.jpg
--------------------------------------------------------------------------------
/design/desktop-design.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/design/desktop-design.jpg
--------------------------------------------------------------------------------
/design/mobile-design.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/design/mobile-design.jpg
--------------------------------------------------------------------------------
/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ayokanmi-Adejola/Time-Tracking-Dashboard/HEAD/images/favicon-32x32.png
--------------------------------------------------------------------------------
/images/icon-ellipsis.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-self-care.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-work.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-study.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-social.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-exercise.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/style-guide.md:
--------------------------------------------------------------------------------
1 | # Front-end Style Guide
2 |
3 | ## Layout
4 |
5 | The designs were created to the following widths:
6 |
7 | - Mobile: 375px
8 | - Desktop: 1440px
9 |
10 | > 💡 These are just the design sizes. Ensure content is responsive and meets WCAG requirements by testing the full range of screen sizes from 320px to large screens.
11 |
12 | ## Colors
13 |
14 | ### Primary
15 |
16 | - Purple 600: hsl(246, 80%, 60%)
17 |
18 | - Orange 300 (work): hsl(15, 100%, 70%)
19 | - Blue 300 (play): hsl(195, 74%, 62%)
20 | - Pink 400 (study): hsl(348, 100%, 68%)
21 | - Green 400 (exercise): hsl(145, 58%, 55%)
22 | - Purple 700 (social): hsl(264, 64%, 52%)
23 | - Yellow 300 (self care): hsl(43, 84%, 65%)
24 |
25 | ### Neutral
26 |
27 | - Navy 950: hsl(226, 43%, 10%)
28 | - Navy 900: hsl(235, 46%, 20%)
29 | - Purple 500: hsl(235, 45%, 61%)
30 | - Navy 200: hsl(236, 100%, 87%)
31 |
32 | ## Typography
33 |
34 | ### Body Copy
35 |
36 | - Font size: 18px (card titles e.g. Work, Play)
37 |
38 | ### Font
39 |
40 | - Family: [Rubik](https://fonts.google.com/specimen/Rubik)
41 | - Weights: 300, 400, 500
42 |
43 | > 💎 [Upgrade to Pro](https://www.frontendmentor.io/pro?ref=style-guide) for design file access to see all design details and get hands-on experience using a professional workflow with tools like Figma.
44 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Frontend Mentor | Time tracking dashboard
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |

18 |
19 |
Report for
20 |
AYOKANMI ADEJOLA
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Work",
4 | "timeframes": {
5 | "daily": {
6 | "current": 5,
7 | "previous": 7
8 | },
9 | "weekly": {
10 | "current": 32,
11 | "previous": 36
12 | },
13 | "monthly": {
14 | "current": 103,
15 | "previous": 128
16 | }
17 | }
18 | },
19 | {
20 | "title": "Play",
21 | "timeframes": {
22 | "daily": {
23 | "current": 1,
24 | "previous": 2
25 | },
26 | "weekly": {
27 | "current": 10,
28 | "previous": 8
29 | },
30 | "monthly": {
31 | "current": 23,
32 | "previous": 29
33 | }
34 | }
35 | },
36 | {
37 | "title": "Study",
38 | "timeframes": {
39 | "daily": {
40 | "current": 0,
41 | "previous": 1
42 | },
43 | "weekly": {
44 | "current": 4,
45 | "previous": 7
46 | },
47 | "monthly": {
48 | "current": 13,
49 | "previous": 19
50 | }
51 | }
52 | },
53 | {
54 | "title": "Exercise",
55 | "timeframes": {
56 | "daily": {
57 | "current": 1,
58 | "previous": 1
59 | },
60 | "weekly": {
61 | "current": 4,
62 | "previous": 5
63 | },
64 | "monthly": {
65 | "current": 11,
66 | "previous": 18
67 | }
68 | }
69 | },
70 | {
71 | "title": "Social",
72 | "timeframes": {
73 | "daily": {
74 | "current": 1,
75 | "previous": 3
76 | },
77 | "weekly": {
78 | "current": 5,
79 | "previous": 10
80 | },
81 | "monthly": {
82 | "current": 21,
83 | "previous": 23
84 | }
85 | }
86 | },
87 | {
88 | "title": "Self Care",
89 | "timeframes": {
90 | "daily": {
91 | "current": 0,
92 | "previous": 1
93 | },
94 | "weekly": {
95 | "current": 2,
96 | "previous": 2
97 | },
98 | "monthly": {
99 | "current": 7,
100 | "previous": 11
101 | }
102 | }
103 | }
104 | ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frontend Mentor - Time tracking dashboard solution
2 |
3 | This is a solution to the [Time tracking dashboard challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/time-tracking-dashboard-UIQ7167Jw). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
4 |
5 | 
6 |
7 | ## Overview
8 |
9 | ### The challenge
10 |
11 | Users should be able to:
12 |
13 | - View the optimal layout for the site depending on their device's screen size
14 | - See hover states for all interactive elements on the page
15 | - Switch between viewing Daily, Weekly, and Monthly stats
16 |
17 | | Desktop | Mobile |
18 | | ------- | ------ |
19 | |
|
|
20 |
21 | ## My process
22 |
23 | ### Built with
24 |
25 | - Semantic HTML5 markup
26 | - CSS custom properties
27 | - Flexbox
28 | - CSS Grid
29 | - Mobile-first workflow
30 | - Vanilla JavaScript
31 |
32 | ### What I learned
33 |
34 | This project helped me improve my CSS Grid skills and JavaScript DOM manipulation. I learned how to create responsive layouts that adapt to different screen sizes and how to dynamically update content based on user interaction.
35 |
36 | I'm particularly proud of the implementation of the time period switching functionality:
37 |
38 | ```js
39 | function setupEventListeners() {
40 | const timeButtons = document.querySelectorAll('.time-btn');
41 |
42 | timeButtons.forEach(button => {
43 | button.addEventListener('click', () => {
44 | timeButtons.forEach(btn => btn.classList.remove('active'));
45 | button.classList.add('active');
46 | currentTimePeriod = button.dataset.time;
47 | renderActivityCards(activityData, currentTimePeriod);
48 | });
49 | });
50 | }
51 | ```
52 |
53 | ### Continued development
54 |
55 | In future projects, I'd like to focus on:
56 | - Improving accessibility features
57 | - Adding animations for smoother transitions
58 | - Implementing more complex data visualization techniques
59 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* Primary Colors */
3 | --purple-600: hsl(246, 80%, 60%);
4 | --orange-300: hsl(15, 100%, 70%); /* work */
5 | --blue-300: hsl(195, 74%, 62%); /* play */
6 | --pink-400: hsl(348, 100%, 68%); /* study */
7 | --green-400: hsl(145, 58%, 55%); /* exercise */
8 | --purple-700: hsl(264, 64%, 52%); /* social */
9 | --yellow-300: hsl(43, 84%, 65%); /* self care */
10 |
11 | /* Neutral Colors */
12 | --navy-950: hsl(226, 43%, 10%);
13 | --navy-900: hsl(235, 46%, 20%);
14 | --purple-500: hsl(235, 45%, 61%);
15 | --navy-200: hsl(236, 100%, 87%);
16 | }
17 |
18 | * {
19 | margin: 0;
20 | padding: 0;
21 | box-sizing: border-box;
22 | }
23 |
24 | body {
25 | font-family: 'Rubik', sans-serif;
26 | font-size: 18px;
27 | background-color: var(--navy-950);
28 | color: white;
29 | min-height: 100vh;
30 | display: flex;
31 | flex-direction: column;
32 | align-items: center;
33 | justify-content: center;
34 | padding: 1.5rem;
35 | }
36 |
37 | .dashboard {
38 | max-width: 1110px;
39 | width: 100%;
40 | display: grid;
41 | gap: 1.5rem;
42 | }
43 |
44 | /* Profile Card Styles */
45 | .profile-card {
46 | background-color: var(--navy-900);
47 | border-radius: 15px;
48 | overflow: hidden;
49 | }
50 |
51 | .profile-info {
52 | background-color: var(--purple-600);
53 | padding: 2rem;
54 | border-radius: 15px;
55 | display: flex;
56 | flex-direction: column;
57 | gap: 1.5rem;
58 | }
59 |
60 | .profile-image {
61 | width: 70px;
62 | height: 70px;
63 | border: 3px solid white;
64 | border-radius: 50%;
65 | }
66 |
67 | .report-for {
68 | color: var(--navy-200);
69 | font-size: 0.9rem;
70 | font-weight: 300;
71 | }
72 |
73 | .profile-name {
74 | font-size: 1.5rem;
75 | font-weight: 300;
76 | }
77 |
78 | .time-period-selector {
79 | padding: 1.5rem;
80 | display: flex;
81 | flex-direction: column;
82 | gap: 1rem;
83 | }
84 |
85 | .time-btn {
86 | background: none;
87 | border: none;
88 | color: var(--purple-500);
89 | font-family: 'Rubik', sans-serif;
90 | font-size: 1rem;
91 | text-align: left;
92 | cursor: pointer;
93 | transition: color 0.3s;
94 | }
95 |
96 | .time-btn:hover {
97 | color: white;
98 | }
99 |
100 | .time-btn.active {
101 | color: white;
102 | }
103 |
104 | /* Activity Card Styles */
105 | .activity-card {
106 | border-radius: 15px;
107 | overflow: hidden;
108 | position: relative;
109 | }
110 |
111 | .activity-card-bg {
112 | height: 45px;
113 | position: relative;
114 | overflow: hidden;
115 | }
116 |
117 | .activity-card-bg img {
118 | position: absolute;
119 | top: -10px;
120 | right: 15px;
121 | }
122 |
123 | .activity-card-content {
124 | background-color: var(--navy-900);
125 | padding: 1.5rem;
126 | border-radius: 15px;
127 | position: relative;
128 | top: -15px;
129 | transition: background-color 0.3s;
130 | }
131 |
132 | .activity-card-content:hover {
133 | background-color: var(--purple-500);
134 | cursor: pointer;
135 | }
136 |
137 | .activity-header {
138 | display: flex;
139 | justify-content: space-between;
140 | align-items: center;
141 | margin-bottom: 1.5rem;
142 | }
143 |
144 | .activity-title {
145 | font-weight: 500;
146 | font-size: 1.1rem;
147 | }
148 |
149 | .ellipsis {
150 | cursor: pointer;
151 | }
152 |
153 | .activity-time {
154 | font-size: 2.5rem;
155 | font-weight: 300;
156 | margin-bottom: 0.5rem;
157 | }
158 |
159 | .activity-previous {
160 | color: var(--navy-200);
161 | font-size: 0.9rem;
162 | }
163 |
164 | /* Color variations for activity cards */
165 | .activity-card.work .activity-card-bg {
166 | background-color: var(--orange-300);
167 | }
168 |
169 | .activity-card.play .activity-card-bg {
170 | background-color: var(--blue-300);
171 | }
172 |
173 | .activity-card.study .activity-card-bg {
174 | background-color: var(--pink-400);
175 | }
176 |
177 | .activity-card.exercise .activity-card-bg {
178 | background-color: var(--green-400);
179 | }
180 |
181 | .activity-card.social .activity-card-bg {
182 | background-color: var(--purple-700);
183 | }
184 |
185 | .activity-card.self-care .activity-card-bg {
186 | background-color: var(--yellow-300);
187 | }
188 |
189 | /* Footer */
190 | .attribution {
191 | font-size: 11px;
192 | text-align: center;
193 | margin-top: 2rem;
194 | color: var(--navy-200);
195 | }
196 |
197 | .attribution a {
198 | color: var(--purple-600);
199 | }
200 |
201 | /* Media Queries */
202 | @media (min-width: 768px) {
203 | .dashboard {
204 | grid-template-columns: repeat(4, 1fr);
205 | }
206 |
207 | .profile-card {
208 | grid-row: span 2;
209 | }
210 |
211 | .profile-info {
212 | padding: 2rem 2rem 4rem;
213 | }
214 |
215 | .profile-image {
216 | width: 80px;
217 | height: 80px;
218 | }
219 |
220 | .profile-name {
221 | font-size: 2.5rem;
222 | }
223 |
224 | .time-period-selector {
225 | padding: 2rem;
226 | }
227 |
228 | .activity-cards {
229 | grid-column: span 3;
230 | display: grid;
231 | grid-template-columns: repeat(3, 1fr);
232 | gap: 1.5rem;
233 | }
234 | }
235 |
236 | @media (max-width: 767px) {
237 | .dashboard {
238 | grid-template-columns: 1fr;
239 | }
240 |
241 | .profile-info {
242 | flex-direction: row;
243 | align-items: center;
244 | gap: 1rem;
245 | }
246 |
247 | .time-period-selector {
248 | flex-direction: row;
249 | justify-content: space-between;
250 | }
251 |
252 | .activity-cards {
253 | display: grid;
254 | grid-template-columns: 1fr;
255 | gap: 1.5rem;
256 | }
257 |
258 | .activity-time {
259 | font-size: 2rem;
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', () => {
2 | // Default time period
3 | let currentTimePeriod = 'weekly';
4 |
5 | // Embed the data directly to avoid CORS issues when opening the file locally
6 | const activityData = [
7 | {
8 | "title": "Work",
9 | "timeframes": {
10 | "daily": {
11 | "current": 5,
12 | "previous": 7
13 | },
14 | "weekly": {
15 | "current": 32,
16 | "previous": 36
17 | },
18 | "monthly": {
19 | "current": 103,
20 | "previous": 128
21 | }
22 | }
23 | },
24 | {
25 | "title": "Play",
26 | "timeframes": {
27 | "daily": {
28 | "current": 1,
29 | "previous": 2
30 | },
31 | "weekly": {
32 | "current": 10,
33 | "previous": 8
34 | },
35 | "monthly": {
36 | "current": 23,
37 | "previous": 29
38 | }
39 | }
40 | },
41 | {
42 | "title": "Study",
43 | "timeframes": {
44 | "daily": {
45 | "current": 2,
46 | "previous": 1
47 | },
48 | "weekly": {
49 | "current": 4,
50 | "previous": 7
51 | },
52 | "monthly": {
53 | "current": 13,
54 | "previous": 19
55 | }
56 | }
57 | },
58 | {
59 | "title": "Exercise",
60 | "timeframes": {
61 | "daily": {
62 | "current": 1,
63 | "previous": 1
64 | },
65 | "weekly": {
66 | "current": 4,
67 | "previous": 5
68 | },
69 | "monthly": {
70 | "current": 11,
71 | "previous": 18
72 | }
73 | }
74 | },
75 | {
76 | "title": "Social",
77 | "timeframes": {
78 | "daily": {
79 | "current": 1,
80 | "previous": 3
81 | },
82 | "weekly": {
83 | "current": 5,
84 | "previous": 10
85 | },
86 | "monthly": {
87 | "current": 21,
88 | "previous": 23
89 | }
90 | }
91 | },
92 | {
93 | "title": "Self Care",
94 | "timeframes": {
95 | "daily": {
96 | "current": 1,
97 | "previous": 1
98 | },
99 | "weekly": {
100 | "current": 2,
101 | "previous": 2
102 | },
103 | "monthly": {
104 | "current": 7,
105 | "previous": 11
106 | }
107 | }
108 | }
109 | ];
110 |
111 | // Initialize the dashboard
112 | renderActivityCards(activityData, currentTimePeriod);
113 | setupEventListeners();
114 |
115 | // Set up event listeners for time period buttons
116 | function setupEventListeners() {
117 | const timeButtons = document.querySelectorAll('.time-btn');
118 |
119 | timeButtons.forEach(button => {
120 | button.addEventListener('click', () => {
121 | // Remove active class from all buttons
122 | timeButtons.forEach(btn => btn.classList.remove('active'));
123 |
124 | // Add active class to clicked button
125 | button.classList.add('active');
126 |
127 | // Update time period and re-render cards
128 | currentTimePeriod = button.dataset.time;
129 | renderActivityCards(activityData, currentTimePeriod);
130 | });
131 | });
132 | }
133 |
134 | // Render activity cards based on the selected time period
135 | function renderActivityCards(data, timePeriod) {
136 | const activityCardsContainer = document.querySelector('.activity-cards');
137 | activityCardsContainer.innerHTML = '';
138 |
139 | data.forEach(activity => {
140 | const title = activity.title;
141 | const currentHours = activity.timeframes[timePeriod].current;
142 | const previousHours = activity.timeframes[timePeriod].previous;
143 |
144 | // Format the title for CSS class (lowercase and hyphenated)
145 | const titleClass = title.toLowerCase().replace(/\s+/g, '-');
146 |
147 | // Determine the icon path based on the activity title
148 | const iconPath = `./images/icon-${titleClass}.svg`;
149 |
150 | // Format hours text (singular vs plural)
151 | const currentHoursText = currentHours === 1 ? '1hr' : `${currentHours}hrs`;
152 | const previousHoursText = previousHours === 1 ? '1hr' : `${previousHours}hrs`;
153 |
154 | // Determine the previous period text based on the time period
155 | let previousPeriodText = '';
156 | switch(timePeriod) {
157 | case 'daily':
158 | previousPeriodText = 'Yesterday';
159 | break;
160 | case 'weekly':
161 | previousPeriodText = 'Last Week';
162 | break;
163 | case 'monthly':
164 | previousPeriodText = 'Last Month';
165 | break;
166 | }
167 |
168 | // Create the activity card HTML
169 | const activityCardHTML = `
170 |
171 |
172 |

173 |
174 |
175 |
179 |
180 |
${currentHoursText}
181 |
${previousPeriodText} - ${previousHoursText}
182 |
183 |
184 |
185 | `;
186 |
187 | activityCardsContainer.innerHTML += activityCardHTML;
188 | });
189 | }
190 | });
191 |
--------------------------------------------------------------------------------