├── 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 | Jeremy Robson 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 | ![](./preview.jpg) 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 | |image|image| 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 | ${title} icon 173 |
174 |
175 |
176 |

${title}

177 | More options 178 |
179 |
180 |

${currentHoursText}

181 |

${previousPeriodText} - ${previousHoursText}

182 |
183 |
184 |
185 | `; 186 | 187 | activityCardsContainer.innerHTML += activityCardHTML; 188 | }); 189 | } 190 | }); 191 | --------------------------------------------------------------------------------