├── .env.development ├── .env.example ├── .env.production ├── .gitignore ├── README.md ├── github-info.png ├── jsconfig.json ├── next.config.js ├── package.json ├── pnpm-lock.yaml ├── public ├── favicon.ico ├── next.svg ├── thirteen.svg └── vercel.svg └── src ├── assets ├── GitHubInsight__Logo.png ├── block-chart.png ├── chart.png ├── github.svg ├── green-ok.png └── ok-down.png ├── components ├── common │ └── Loading.js ├── follow │ ├── FollowItem.js │ ├── followerCard.js │ ├── followersDiv.js │ ├── followingCard.js │ ├── index.js │ ├── loading.js │ ├── noUserMessage.js │ ├── nonFollowers.js │ └── style.module.scss ├── fusioncharts │ ├── FusionChart.js │ └── styles.module.scss ├── search │ ├── index.js │ └── styles.module.scss ├── stats │ ├── StatsCard.js │ ├── StatsCartdtem.js │ └── styles.module.scss ├── table │ ├── table-mobile.js │ ├── table-mobile.module.scss │ ├── table.js │ └── table.module.scss ├── title │ ├── styles.module.scss │ └── title.js └── userCard │ ├── index.js │ └── styles.module.scss ├── container ├── dashboard │ ├── followerCardContainer │ │ └── index.js │ ├── index.js │ └── styles.module.scss ├── home │ ├── about │ │ ├── index.js │ │ └── styles.module.scss │ ├── feature │ │ ├── index.js │ │ └── styles.module.scss │ ├── hero │ │ ├── index.js │ │ └── styles.module.scss │ ├── index.js │ └── styles.module.scss ├── nonFollowers │ ├── container │ │ └── Follower.js │ ├── index.js │ └── nonFollowers.module.scss ├── rate │ └── index.js └── repos │ └── index.js ├── data ├── fusion-configs.js └── stats-card-item-data.js ├── helper ├── findLanguageData.js ├── getAllData.js └── sortData.js ├── icons └── index.js ├── layout ├── footer │ ├── index.js │ └── styles.module.scss ├── header │ ├── hamburgerBox.js │ ├── index.js │ ├── logo.js │ ├── mobileMenu.js │ ├── navigation.js │ ├── sidebarLeft.js │ ├── sidebarRight.js │ └── styles.module.scss └── info │ ├── index.js │ └── styles.module.scss ├── pages ├── _app.js ├── _document.js ├── api │ ├── followers.js │ ├── following.js │ └── repos.js ├── dashboard.js ├── index.js ├── non-followers.js ├── rate.js └── repos.js ├── redux ├── store.js ├── theme.js └── usersSlice.js ├── services ├── rate.js └── users.js └── styles ├── _reset.scss ├── _variables.scss └── globals.scss /.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API=https://api.github.com/ 2 | 3 | NEXT_PUBLIC_FOLLOWERS_PER_PAGE=100 4 | NEXT_PUBLIC_FOLLOWING_PER_PAGE=100 5 | NEXT_PUBLIC_REPOS_PER_PAGE=100 6 | 7 | NEXT_PUBLIC_MOCK_API=http://localhost:3000/api 8 | 9 | 10 | NEXT_PUBLIC_PROJECT_GITHUB_URL=https://github.com/yasinelbuz/github-insight -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API= 2 | 3 | NEXT_PUBLIC_FOLLOWERS_PER_PAGE= 4 | NEXT_PUBLIC_FOLLOWING_PER_PAGE= 5 | NEXT_PUBLIC_REPOS_PER_PAGE= 6 | 7 | NEXT_PUBLIC_MOCK_API= 8 | 9 | 10 | NEXT_PUBLIC_PROJECT_GITHUB_URL= -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API=https://api.github.com/ 2 | 3 | NEXT_PUBLIC_FOLLOWERS_PER_PAGE=100 4 | NEXT_PUBLIC_FOLLOWING_PER_PAGE=100 5 | NEXT_PUBLIC_REPOS_PER_PAGE=100 6 | 7 | NEXT_PUBLIC_MOCK_API=http://localhost:3000/api 8 | 9 | 10 | NEXT_PUBLIC_PROJECT_GITHUB_URL=https://github.com/yasinelbuz/github-insight -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env 30 | .env.development 31 | .env.production 32 | .env.local 33 | .env.development.local 34 | .env.test.local 35 | .env.production.local 36 | 37 | 38 | # vercel 39 | .vercel 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Github Insight 2 | 3 | _"Github Insight”_ 4 | 5 | They can view their profile information, number of followers and followed users, public and private projects, repo names, star ratings, and recent activity on GitHub. 6 | 7 | - [Demo](#demo) 8 | - [Installation](#installation) 9 | - [Starting project](#starting-project) 10 | - [Links](#links) 11 | - [Contact](#contact) 12 | 13 | ## Demo 14 | 15 | ## ![githubInfo web](https://github.com/yasinelbuz/github-info/blob/master/github-info.png) 16 | 17 | ## Installation 18 | 19 | ```bash 20 | ** open terminal 21 | git clone https://github.com/yasinelbuz/github-info.git 22 | 23 | ** then 24 | ** install packages 25 | pnpm install 26 | ``` 27 | 28 | ## Starting project 29 | 30 | ```bash 31 | pnpm run dev 32 | 33 | ** open web page 34 | http://localhost:3000 35 | ``` 36 | 37 | ## Links 38 | 39 | - [Github](https://github.com/yasinelbuz/github-insight) 40 | - [Deployment](https://githubinsight.vercel.app/) 41 | 42 | ## Contact 43 | 44 | You can contact me at yasinelbuz@gmail.com 45 | -------------------------------------------------------------------------------- /github-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/github-info.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-info", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@react-hook/media-query": "^1.1.1", 13 | "@reduxjs/toolkit": "^1.9.3", 14 | "fusioncharts": "^3.19.0", 15 | "next": "13.2.4", 16 | "react": "18.2.0", 17 | "react-dom": "18.2.0", 18 | "react-fusioncharts": "^4.0.0", 19 | "react-hook": "^0.0.1", 20 | "react-icons": "^4.8.0", 21 | "react-redux": "^8.0.5", 22 | "sass": "^1.59.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@react-hook/media-query': ^1.1.1 5 | '@reduxjs/toolkit': ^1.9.3 6 | fusioncharts: ^3.19.0 7 | next: 13.2.4 8 | react: 18.2.0 9 | react-dom: 18.2.0 10 | react-fusioncharts: ^4.0.0 11 | react-hook: ^0.0.1 12 | react-icons: ^4.8.0 13 | react-redux: ^8.0.5 14 | sass: ^1.59.3 15 | 16 | dependencies: 17 | '@react-hook/media-query': 1.1.1_react@18.2.0 18 | '@reduxjs/toolkit': 1.9.3_k4ae6lp43ej6mezo3ztvx6pykq 19 | fusioncharts: 3.20.0 20 | next: 13.2.4_dpxg4zawgzznnxdt7it3f5d76m 21 | react: 18.2.0 22 | react-dom: 18.2.0_react@18.2.0 23 | react-fusioncharts: 4.0.0_react@18.2.0 24 | react-hook: 0.0.1 25 | react-icons: 4.8.0_react@18.2.0 26 | react-redux: 8.0.5_biqbaboplfbrettd7655fr4n2y 27 | sass: 1.59.3 28 | 29 | packages: 30 | 31 | /@babel/runtime/7.21.0: 32 | resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} 33 | engines: {node: '>=6.9.0'} 34 | dependencies: 35 | regenerator-runtime: 0.13.11 36 | dev: false 37 | 38 | /@fusioncharts/accessibility/1.7.0: 39 | resolution: {integrity: sha512-CtRzac90AMP+JJSSMgICwsIRw2W20fQ8YXpCKd8cmrglXlVThRgHqlcQCUHx5/3Dbzg6KXkA57ZiICVBXWudKg==} 40 | dev: false 41 | 42 | /@fusioncharts/charts/3.20.0: 43 | resolution: {integrity: sha512-1yCQ6hroXYn/UPsguw8yUxrTKoBdP/n4whAN66msPnBmoeCY0cFt86Y33fMZVHpB8iVbg06OYxk0JJeuNQODWg==} 44 | dependencies: 45 | '@babel/runtime': 7.21.0 46 | '@fusioncharts/core': 1.7.0 47 | '@fusioncharts/features': 1.7.0 48 | '@fusioncharts/utils': 1.7.0 49 | dev: false 50 | 51 | /@fusioncharts/constructor/1.7.0: 52 | resolution: {integrity: sha512-RQJylJGIDwvzkLI4ouM6lRuNqrdwT6I6Dn7denvEkReAQ3bZgfF14DxiexdeJyZGF5YQhsvQwRa/VJDghPvm5w==} 53 | dependencies: 54 | '@babel/runtime': 7.21.0 55 | '@fusioncharts/core': 1.7.0 56 | '@fusioncharts/maps': 3.20.0 57 | dev: false 58 | 59 | /@fusioncharts/core/1.7.0: 60 | resolution: {integrity: sha512-pxf/LY7OG8eP/UhFB4XS0E7XFFsKTwL90j9K0XFDtzmPVx8JLR30s2azv6co4A60nHeg5fZQ6i2Dp0M/Vq7Uaw==} 61 | dependencies: 62 | '@babel/runtime': 7.21.0 63 | '@fusioncharts/utils': 1.7.0 64 | core-js: 3.29.1 65 | ramda: 0.25.0 66 | dev: false 67 | 68 | /@fusioncharts/datatable/1.7.0: 69 | resolution: {integrity: sha512-MaAiRKEEzj7rG7iRBBNCxn3e4msAIwuBezgnVyCczUWb/NrXKJVPzXPr5bqgLpmW7dpso3Q+7zfd+4ZWs4VBWg==} 70 | dependencies: 71 | '@babel/runtime': 7.21.0 72 | '@fusioncharts/utils': 1.7.0 73 | dev: false 74 | 75 | /@fusioncharts/features/1.7.0: 76 | resolution: {integrity: sha512-kuU8nuVUPVd/xlHZp0USnf7AL1gHyQRj7ld99STRoH632vTo19qGjqHOzH38hOFfZNrTP9nnPg7eIjxC5+qXZw==} 77 | dependencies: 78 | '@babel/runtime': 7.21.0 79 | '@fusioncharts/core': 1.7.0 80 | '@fusioncharts/utils': 1.7.0 81 | jspdf: 2.5.1 82 | dev: false 83 | 84 | /@fusioncharts/fusiontime/2.8.0: 85 | resolution: {integrity: sha512-RfT/AJgQVrZ1GPbEKQDWIObesF/U6xbf9OoabPa4fpZGpgC37OSzSxCDGWOUC58YLKIQ9TIA8cV7vnUDLFi1NA==} 86 | dependencies: 87 | '@babel/runtime': 7.21.0 88 | '@fusioncharts/charts': 3.20.0 89 | '@fusioncharts/core': 1.7.0 90 | '@fusioncharts/datatable': 1.7.0 91 | '@fusioncharts/utils': 1.7.0 92 | ramda: 0.25.0 93 | dev: false 94 | 95 | /@fusioncharts/maps/3.20.0: 96 | resolution: {integrity: sha512-oHz36nYhxR2wJDSoL0LNOH1a4tQ959tLTCPQ1POqjxVscs/F/J/KTQp7CeuoDpBR7lMH4k/XZCVxeeklSrmjEA==} 97 | dependencies: 98 | '@babel/runtime': 7.21.0 99 | '@fusioncharts/charts': 3.20.0 100 | '@fusioncharts/core': 1.7.0 101 | '@fusioncharts/features': 1.7.0 102 | dev: false 103 | 104 | /@fusioncharts/powercharts/3.20.0: 105 | resolution: {integrity: sha512-wNABGvKv1u6a6MNOZWWyuGOQlsRrDKEMFwSUX+yvw3cJ4s3HfBeyXGBP73eJ2T4dcpzxdKdsJU2WdZIbOYeQQw==} 106 | dependencies: 107 | '@babel/runtime': 7.21.0 108 | '@fusioncharts/charts': 3.20.0 109 | '@fusioncharts/core': 1.7.0 110 | '@fusioncharts/utils': 1.7.0 111 | dev: false 112 | 113 | /@fusioncharts/utils/1.7.0: 114 | resolution: {integrity: sha512-GFaQAdGvLtI73T3jtFYoowi2UCQrt86cd8xz/JhrmPgE+BYotiJFGxBJNNilBIjAbRRVbL8bbgSwDNmpXjPkow==} 115 | dependencies: 116 | '@babel/runtime': 7.21.0 117 | ramda: 0.25.0 118 | dev: false 119 | 120 | /@fusioncharts/widgets/3.20.0: 121 | resolution: {integrity: sha512-xEKJ6EJRbdScXPbD3hVU4FLgzXl0FvKs25g/6LP+m4QYHsBdHyEsGTQM5UYALvYRIqfvj69spnmU3qN+7nsw6Q==} 122 | dependencies: 123 | '@babel/runtime': 7.21.0 124 | '@fusioncharts/charts': 3.20.0 125 | '@fusioncharts/constructor': 1.7.0 126 | '@fusioncharts/core': 1.7.0 127 | '@fusioncharts/features': 1.7.0 128 | '@fusioncharts/utils': 1.7.0 129 | dev: false 130 | 131 | /@next/env/13.2.4: 132 | resolution: {integrity: sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA==} 133 | dev: false 134 | 135 | /@next/swc-android-arm-eabi/13.2.4: 136 | resolution: {integrity: sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==} 137 | engines: {node: '>= 10'} 138 | cpu: [arm] 139 | os: [android] 140 | requiresBuild: true 141 | dev: false 142 | optional: true 143 | 144 | /@next/swc-android-arm64/13.2.4: 145 | resolution: {integrity: sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==} 146 | engines: {node: '>= 10'} 147 | cpu: [arm64] 148 | os: [android] 149 | requiresBuild: true 150 | dev: false 151 | optional: true 152 | 153 | /@next/swc-darwin-arm64/13.2.4: 154 | resolution: {integrity: sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A==} 155 | engines: {node: '>= 10'} 156 | cpu: [arm64] 157 | os: [darwin] 158 | requiresBuild: true 159 | dev: false 160 | optional: true 161 | 162 | /@next/swc-darwin-x64/13.2.4: 163 | resolution: {integrity: sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw==} 164 | engines: {node: '>= 10'} 165 | cpu: [x64] 166 | os: [darwin] 167 | requiresBuild: true 168 | dev: false 169 | optional: true 170 | 171 | /@next/swc-freebsd-x64/13.2.4: 172 | resolution: {integrity: sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==} 173 | engines: {node: '>= 10'} 174 | cpu: [x64] 175 | os: [freebsd] 176 | requiresBuild: true 177 | dev: false 178 | optional: true 179 | 180 | /@next/swc-linux-arm-gnueabihf/13.2.4: 181 | resolution: {integrity: sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==} 182 | engines: {node: '>= 10'} 183 | cpu: [arm] 184 | os: [linux] 185 | requiresBuild: true 186 | dev: false 187 | optional: true 188 | 189 | /@next/swc-linux-arm64-gnu/13.2.4: 190 | resolution: {integrity: sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg==} 191 | engines: {node: '>= 10'} 192 | cpu: [arm64] 193 | os: [linux] 194 | requiresBuild: true 195 | dev: false 196 | optional: true 197 | 198 | /@next/swc-linux-arm64-musl/13.2.4: 199 | resolution: {integrity: sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw==} 200 | engines: {node: '>= 10'} 201 | cpu: [arm64] 202 | os: [linux] 203 | requiresBuild: true 204 | dev: false 205 | optional: true 206 | 207 | /@next/swc-linux-x64-gnu/13.2.4: 208 | resolution: {integrity: sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ==} 209 | engines: {node: '>= 10'} 210 | cpu: [x64] 211 | os: [linux] 212 | requiresBuild: true 213 | dev: false 214 | optional: true 215 | 216 | /@next/swc-linux-x64-musl/13.2.4: 217 | resolution: {integrity: sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==} 218 | engines: {node: '>= 10'} 219 | cpu: [x64] 220 | os: [linux] 221 | requiresBuild: true 222 | dev: false 223 | optional: true 224 | 225 | /@next/swc-win32-arm64-msvc/13.2.4: 226 | resolution: {integrity: sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==} 227 | engines: {node: '>= 10'} 228 | cpu: [arm64] 229 | os: [win32] 230 | requiresBuild: true 231 | dev: false 232 | optional: true 233 | 234 | /@next/swc-win32-ia32-msvc/13.2.4: 235 | resolution: {integrity: sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==} 236 | engines: {node: '>= 10'} 237 | cpu: [ia32] 238 | os: [win32] 239 | requiresBuild: true 240 | dev: false 241 | optional: true 242 | 243 | /@next/swc-win32-x64-msvc/13.2.4: 244 | resolution: {integrity: sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==} 245 | engines: {node: '>= 10'} 246 | cpu: [x64] 247 | os: [win32] 248 | requiresBuild: true 249 | dev: false 250 | optional: true 251 | 252 | /@react-hook/media-query/1.1.1_react@18.2.0: 253 | resolution: {integrity: sha512-VM14wDOX5CW5Dn6b2lTiMd79BFMTut9AZj2+vIRT3LCKgMCYmdqruTtzDPSnIVDQdtxdPgtOzvU9oK20LopuOw==} 254 | peerDependencies: 255 | react: '>=16.8' 256 | dependencies: 257 | react: 18.2.0 258 | dev: false 259 | 260 | /@reduxjs/toolkit/1.9.3_k4ae6lp43ej6mezo3ztvx6pykq: 261 | resolution: {integrity: sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==} 262 | peerDependencies: 263 | react: ^16.9.0 || ^17.0.0 || ^18 264 | react-redux: ^7.2.1 || ^8.0.2 265 | peerDependenciesMeta: 266 | react: 267 | optional: true 268 | react-redux: 269 | optional: true 270 | dependencies: 271 | immer: 9.0.19 272 | react: 18.2.0 273 | react-redux: 8.0.5_biqbaboplfbrettd7655fr4n2y 274 | redux: 4.2.1 275 | redux-thunk: 2.4.2_redux@4.2.1 276 | reselect: 4.1.7 277 | dev: false 278 | 279 | /@swc/helpers/0.4.14: 280 | resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} 281 | dependencies: 282 | tslib: 2.5.0 283 | dev: false 284 | 285 | /@types/hoist-non-react-statics/3.3.1: 286 | resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} 287 | dependencies: 288 | '@types/react': 18.0.28 289 | hoist-non-react-statics: 3.3.2 290 | dev: false 291 | 292 | /@types/prop-types/15.7.5: 293 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} 294 | dev: false 295 | 296 | /@types/raf/3.4.0: 297 | resolution: {integrity: sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==} 298 | dev: false 299 | optional: true 300 | 301 | /@types/react/18.0.28: 302 | resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} 303 | dependencies: 304 | '@types/prop-types': 15.7.5 305 | '@types/scheduler': 0.16.2 306 | csstype: 3.1.1 307 | dev: false 308 | 309 | /@types/scheduler/0.16.2: 310 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} 311 | dev: false 312 | 313 | /@types/use-sync-external-store/0.0.3: 314 | resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} 315 | dev: false 316 | 317 | /anymatch/3.1.3: 318 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 319 | engines: {node: '>= 8'} 320 | dependencies: 321 | normalize-path: 3.0.0 322 | picomatch: 2.3.1 323 | dev: false 324 | 325 | /atob/2.1.2: 326 | resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} 327 | engines: {node: '>= 4.5.0'} 328 | hasBin: true 329 | dev: false 330 | 331 | /base64-arraybuffer/1.0.2: 332 | resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} 333 | engines: {node: '>= 0.6.0'} 334 | dev: false 335 | optional: true 336 | 337 | /binary-extensions/2.2.0: 338 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 339 | engines: {node: '>=8'} 340 | dev: false 341 | 342 | /braces/3.0.2: 343 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 344 | engines: {node: '>=8'} 345 | dependencies: 346 | fill-range: 7.0.1 347 | dev: false 348 | 349 | /btoa/1.2.1: 350 | resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} 351 | engines: {node: '>= 0.4.0'} 352 | hasBin: true 353 | dev: false 354 | 355 | /caniuse-lite/1.0.30001469: 356 | resolution: {integrity: sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==} 357 | dev: false 358 | 359 | /canvg/3.0.10: 360 | resolution: {integrity: sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==} 361 | engines: {node: '>=10.0.0'} 362 | requiresBuild: true 363 | dependencies: 364 | '@babel/runtime': 7.21.0 365 | '@types/raf': 3.4.0 366 | core-js: 3.29.1 367 | raf: 3.4.1 368 | regenerator-runtime: 0.13.11 369 | rgbcolor: 1.0.1 370 | stackblur-canvas: 2.5.0 371 | svg-pathdata: 6.0.3 372 | dev: false 373 | optional: true 374 | 375 | /chokidar/3.5.3: 376 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 377 | engines: {node: '>= 8.10.0'} 378 | dependencies: 379 | anymatch: 3.1.3 380 | braces: 3.0.2 381 | glob-parent: 5.1.2 382 | is-binary-path: 2.1.0 383 | is-glob: 4.0.3 384 | normalize-path: 3.0.0 385 | readdirp: 3.6.0 386 | optionalDependencies: 387 | fsevents: 2.3.2 388 | dev: false 389 | 390 | /client-only/0.0.1: 391 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 392 | dev: false 393 | 394 | /core-js/3.29.1: 395 | resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==} 396 | requiresBuild: true 397 | dev: false 398 | 399 | /css-line-break/2.1.0: 400 | resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} 401 | dependencies: 402 | utrie: 1.0.2 403 | dev: false 404 | optional: true 405 | 406 | /csstype/3.1.1: 407 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} 408 | dev: false 409 | 410 | /dompurify/2.4.5: 411 | resolution: {integrity: sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==} 412 | requiresBuild: true 413 | dev: false 414 | optional: true 415 | 416 | /fflate/0.4.8: 417 | resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} 418 | dev: false 419 | 420 | /fill-range/7.0.1: 421 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 422 | engines: {node: '>=8'} 423 | dependencies: 424 | to-regex-range: 5.0.1 425 | dev: false 426 | 427 | /fsevents/2.3.2: 428 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 429 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 430 | os: [darwin] 431 | requiresBuild: true 432 | dev: false 433 | optional: true 434 | 435 | /fusioncharts/3.20.0: 436 | resolution: {integrity: sha512-etMdp8SeFBfRpecoZnCUkrsqu68SBJwUzTWmGu3+1lcMLFDjSnhYXCDuDfQvM+OxljBCNmEPGKA41fYRO8SdQA==} 437 | dependencies: 438 | '@babel/runtime': 7.21.0 439 | '@fusioncharts/accessibility': 1.7.0 440 | '@fusioncharts/charts': 3.20.0 441 | '@fusioncharts/constructor': 1.7.0 442 | '@fusioncharts/core': 1.7.0 443 | '@fusioncharts/datatable': 1.7.0 444 | '@fusioncharts/features': 1.7.0 445 | '@fusioncharts/fusiontime': 2.8.0 446 | '@fusioncharts/maps': 3.20.0 447 | '@fusioncharts/powercharts': 3.20.0 448 | '@fusioncharts/utils': 1.7.0 449 | '@fusioncharts/widgets': 3.20.0 450 | mutationobserver-shim: 0.3.7 451 | promise-polyfill: 8.3.0 452 | dev: false 453 | 454 | /glob-parent/5.1.2: 455 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 456 | engines: {node: '>= 6'} 457 | dependencies: 458 | is-glob: 4.0.3 459 | dev: false 460 | 461 | /hoist-non-react-statics/3.3.2: 462 | resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} 463 | dependencies: 464 | react-is: 16.13.1 465 | dev: false 466 | 467 | /html2canvas/1.4.1: 468 | resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} 469 | engines: {node: '>=8.0.0'} 470 | requiresBuild: true 471 | dependencies: 472 | css-line-break: 2.1.0 473 | text-segmentation: 1.0.3 474 | dev: false 475 | optional: true 476 | 477 | /immer/9.0.19: 478 | resolution: {integrity: sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==} 479 | dev: false 480 | 481 | /immutable/4.3.0: 482 | resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} 483 | dev: false 484 | 485 | /is-binary-path/2.1.0: 486 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 487 | engines: {node: '>=8'} 488 | dependencies: 489 | binary-extensions: 2.2.0 490 | dev: false 491 | 492 | /is-extglob/2.1.1: 493 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 494 | engines: {node: '>=0.10.0'} 495 | dev: false 496 | 497 | /is-glob/4.0.3: 498 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 499 | engines: {node: '>=0.10.0'} 500 | dependencies: 501 | is-extglob: 2.1.1 502 | dev: false 503 | 504 | /is-number/7.0.0: 505 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 506 | engines: {node: '>=0.12.0'} 507 | dev: false 508 | 509 | /js-tokens/4.0.0: 510 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 511 | dev: false 512 | 513 | /jspdf/2.5.1: 514 | resolution: {integrity: sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==} 515 | dependencies: 516 | '@babel/runtime': 7.21.0 517 | atob: 2.1.2 518 | btoa: 1.2.1 519 | fflate: 0.4.8 520 | optionalDependencies: 521 | canvg: 3.0.10 522 | core-js: 3.29.1 523 | dompurify: 2.4.5 524 | html2canvas: 1.4.1 525 | dev: false 526 | 527 | /loose-envify/1.4.0: 528 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 529 | hasBin: true 530 | dependencies: 531 | js-tokens: 4.0.0 532 | dev: false 533 | 534 | /mutationobserver-shim/0.3.7: 535 | resolution: {integrity: sha512-oRIDTyZQU96nAiz2AQyngwx1e89iApl2hN5AOYwyxLUB47UYsU3Wv9lJWqH5y/QdiYkc5HQLi23ZNB3fELdHcQ==} 536 | dev: false 537 | 538 | /nanoid/3.3.4: 539 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 540 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 541 | hasBin: true 542 | dev: false 543 | 544 | /next/13.2.4_dpxg4zawgzznnxdt7it3f5d76m: 545 | resolution: {integrity: sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==} 546 | engines: {node: '>=14.6.0'} 547 | hasBin: true 548 | peerDependencies: 549 | '@opentelemetry/api': ^1.4.0 550 | fibers: '>= 3.1.0' 551 | node-sass: ^6.0.0 || ^7.0.0 552 | react: ^18.2.0 553 | react-dom: ^18.2.0 554 | sass: ^1.3.0 555 | peerDependenciesMeta: 556 | '@opentelemetry/api': 557 | optional: true 558 | fibers: 559 | optional: true 560 | node-sass: 561 | optional: true 562 | sass: 563 | optional: true 564 | dependencies: 565 | '@next/env': 13.2.4 566 | '@swc/helpers': 0.4.14 567 | caniuse-lite: 1.0.30001469 568 | postcss: 8.4.14 569 | react: 18.2.0 570 | react-dom: 18.2.0_react@18.2.0 571 | sass: 1.59.3 572 | styled-jsx: 5.1.1_react@18.2.0 573 | optionalDependencies: 574 | '@next/swc-android-arm-eabi': 13.2.4 575 | '@next/swc-android-arm64': 13.2.4 576 | '@next/swc-darwin-arm64': 13.2.4 577 | '@next/swc-darwin-x64': 13.2.4 578 | '@next/swc-freebsd-x64': 13.2.4 579 | '@next/swc-linux-arm-gnueabihf': 13.2.4 580 | '@next/swc-linux-arm64-gnu': 13.2.4 581 | '@next/swc-linux-arm64-musl': 13.2.4 582 | '@next/swc-linux-x64-gnu': 13.2.4 583 | '@next/swc-linux-x64-musl': 13.2.4 584 | '@next/swc-win32-arm64-msvc': 13.2.4 585 | '@next/swc-win32-ia32-msvc': 13.2.4 586 | '@next/swc-win32-x64-msvc': 13.2.4 587 | transitivePeerDependencies: 588 | - '@babel/core' 589 | - babel-plugin-macros 590 | dev: false 591 | 592 | /normalize-path/3.0.0: 593 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 594 | engines: {node: '>=0.10.0'} 595 | dev: false 596 | 597 | /performance-now/2.1.0: 598 | resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} 599 | dev: false 600 | optional: true 601 | 602 | /picocolors/1.0.0: 603 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 604 | dev: false 605 | 606 | /picomatch/2.3.1: 607 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 608 | engines: {node: '>=8.6'} 609 | dev: false 610 | 611 | /postcss/8.4.14: 612 | resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} 613 | engines: {node: ^10 || ^12 || >=14} 614 | dependencies: 615 | nanoid: 3.3.4 616 | picocolors: 1.0.0 617 | source-map-js: 1.0.2 618 | dev: false 619 | 620 | /promise-polyfill/8.3.0: 621 | resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==} 622 | dev: false 623 | 624 | /raf/3.4.1: 625 | resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} 626 | dependencies: 627 | performance-now: 2.1.0 628 | dev: false 629 | optional: true 630 | 631 | /ramda/0.25.0: 632 | resolution: {integrity: sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==} 633 | dev: false 634 | 635 | /react-dom/18.2.0_react@18.2.0: 636 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 637 | peerDependencies: 638 | react: ^18.2.0 639 | dependencies: 640 | loose-envify: 1.4.0 641 | react: 18.2.0 642 | scheduler: 0.23.0 643 | dev: false 644 | 645 | /react-fusioncharts/4.0.0_react@18.2.0: 646 | resolution: {integrity: sha512-XPYLfDZp4+UT6/jziFPZbdVbpS0g4kseZkEaB2/CnnB83vsFeBNISP96jjL3lV8SXC5CuWmanELtwqQ+G8mhEg==} 647 | peerDependencies: 648 | react: 0.14.0 - 18.x 649 | dependencies: 650 | react: 18.2.0 651 | uuid: 3.4.0 652 | dev: false 653 | 654 | /react-hook/0.0.1: 655 | resolution: {integrity: sha512-2/Guf88/dGyFgUT7QDtBJ1l7V5yqTcAHlNRIZNTu2xg0KkDjaiYZp79ah49NDaLMI/J7voWcKLU8wMONG4A/1g==} 656 | dev: false 657 | 658 | /react-icons/4.8.0_react@18.2.0: 659 | resolution: {integrity: sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==} 660 | peerDependencies: 661 | react: '*' 662 | dependencies: 663 | react: 18.2.0 664 | dev: false 665 | 666 | /react-is/16.13.1: 667 | resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} 668 | dev: false 669 | 670 | /react-is/18.2.0: 671 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} 672 | dev: false 673 | 674 | /react-redux/8.0.5_biqbaboplfbrettd7655fr4n2y: 675 | resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} 676 | peerDependencies: 677 | '@types/react': ^16.8 || ^17.0 || ^18.0 678 | '@types/react-dom': ^16.8 || ^17.0 || ^18.0 679 | react: ^16.8 || ^17.0 || ^18.0 680 | react-dom: ^16.8 || ^17.0 || ^18.0 681 | react-native: '>=0.59' 682 | redux: ^4 683 | peerDependenciesMeta: 684 | '@types/react': 685 | optional: true 686 | '@types/react-dom': 687 | optional: true 688 | react-dom: 689 | optional: true 690 | react-native: 691 | optional: true 692 | redux: 693 | optional: true 694 | dependencies: 695 | '@babel/runtime': 7.21.0 696 | '@types/hoist-non-react-statics': 3.3.1 697 | '@types/use-sync-external-store': 0.0.3 698 | hoist-non-react-statics: 3.3.2 699 | react: 18.2.0 700 | react-dom: 18.2.0_react@18.2.0 701 | react-is: 18.2.0 702 | use-sync-external-store: 1.2.0_react@18.2.0 703 | dev: false 704 | 705 | /react/18.2.0: 706 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 707 | engines: {node: '>=0.10.0'} 708 | dependencies: 709 | loose-envify: 1.4.0 710 | dev: false 711 | 712 | /readdirp/3.6.0: 713 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 714 | engines: {node: '>=8.10.0'} 715 | dependencies: 716 | picomatch: 2.3.1 717 | dev: false 718 | 719 | /redux-thunk/2.4.2_redux@4.2.1: 720 | resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} 721 | peerDependencies: 722 | redux: ^4 723 | dependencies: 724 | redux: 4.2.1 725 | dev: false 726 | 727 | /redux/4.2.1: 728 | resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} 729 | dependencies: 730 | '@babel/runtime': 7.21.0 731 | dev: false 732 | 733 | /regenerator-runtime/0.13.11: 734 | resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} 735 | dev: false 736 | 737 | /reselect/4.1.7: 738 | resolution: {integrity: sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==} 739 | dev: false 740 | 741 | /rgbcolor/1.0.1: 742 | resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} 743 | engines: {node: '>= 0.8.15'} 744 | dev: false 745 | optional: true 746 | 747 | /sass/1.59.3: 748 | resolution: {integrity: sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==} 749 | engines: {node: '>=12.0.0'} 750 | hasBin: true 751 | dependencies: 752 | chokidar: 3.5.3 753 | immutable: 4.3.0 754 | source-map-js: 1.0.2 755 | dev: false 756 | 757 | /scheduler/0.23.0: 758 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 759 | dependencies: 760 | loose-envify: 1.4.0 761 | dev: false 762 | 763 | /source-map-js/1.0.2: 764 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 765 | engines: {node: '>=0.10.0'} 766 | dev: false 767 | 768 | /stackblur-canvas/2.5.0: 769 | resolution: {integrity: sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==} 770 | engines: {node: '>=0.1.14'} 771 | dev: false 772 | optional: true 773 | 774 | /styled-jsx/5.1.1_react@18.2.0: 775 | resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} 776 | engines: {node: '>= 12.0.0'} 777 | peerDependencies: 778 | '@babel/core': '*' 779 | babel-plugin-macros: '*' 780 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' 781 | peerDependenciesMeta: 782 | '@babel/core': 783 | optional: true 784 | babel-plugin-macros: 785 | optional: true 786 | dependencies: 787 | client-only: 0.0.1 788 | react: 18.2.0 789 | dev: false 790 | 791 | /svg-pathdata/6.0.3: 792 | resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==} 793 | engines: {node: '>=12.0.0'} 794 | dev: false 795 | optional: true 796 | 797 | /text-segmentation/1.0.3: 798 | resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} 799 | dependencies: 800 | utrie: 1.0.2 801 | dev: false 802 | optional: true 803 | 804 | /to-regex-range/5.0.1: 805 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 806 | engines: {node: '>=8.0'} 807 | dependencies: 808 | is-number: 7.0.0 809 | dev: false 810 | 811 | /tslib/2.5.0: 812 | resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} 813 | dev: false 814 | 815 | /use-sync-external-store/1.2.0_react@18.2.0: 816 | resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} 817 | peerDependencies: 818 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 819 | dependencies: 820 | react: 18.2.0 821 | dev: false 822 | 823 | /utrie/1.0.2: 824 | resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} 825 | dependencies: 826 | base64-arraybuffer: 1.0.2 827 | dev: false 828 | optional: true 829 | 830 | /uuid/3.4.0: 831 | resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} 832 | deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. 833 | hasBin: true 834 | dev: false 835 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/public/favicon.ico -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/GitHubInsight__Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/assets/GitHubInsight__Logo.png -------------------------------------------------------------------------------- /src/assets/block-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/assets/block-chart.png -------------------------------------------------------------------------------- /src/assets/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/assets/chart.png -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/green-ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/assets/green-ok.png -------------------------------------------------------------------------------- /src/assets/ok-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/assets/ok-down.png -------------------------------------------------------------------------------- /src/components/common/Loading.js: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return ( 3 |
4 |
5 |
6 |
7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/follow/FollowItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './style.module.scss'; 3 | const FollowItem = ({ item }) => { 4 | return ( 5 |
6 | {styles.login} 11 |
12 |

{item?.login}

13 | 18 | {item.html_url} 19 | 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default FollowItem; 26 | -------------------------------------------------------------------------------- /src/components/follow/followerCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './style.module.scss'; 3 | import Loading from "./loading"; 4 | import FollowersDiv from "./followersDiv"; 5 | import FollowItem from "./FollowItem"; 6 | import { useDispatch, useSelector } from "react-redux"; 7 | import { setFollowerPage } from "@/redux/usersSlice"; 8 | import NoUserMessage from "./noUserMessage"; 9 | import { useGetGithubUserByFollowersQuery } from "@/services/users"; 10 | 11 | export default function FollowerCard() { 12 | 13 | const { search, followerPage, followers, following } = useSelector(state => state.users) 14 | const { data, isLoading, isFetching, isSuccess } = useGetGithubUserByFollowersQuery({ name: search, followerPage }, { 15 | refetchOnMountOrArgChange: true, 16 | }); 17 | const dispatch = useDispatch(); 18 | 19 | const changePageNumber = () => { 20 | if (isSuccess) { 21 | dispatch(setFollowerPage(1)) 22 | } 23 | } 24 | 25 | const checkData = (data) => data?.length != 0 && data?.length == process.env.NEXT_PUBLIC_FOLLOWERS_PER_PAGE 26 | 27 | const button = 30 | 31 | const followersMap = followers?.map(item => ) 32 | 33 | const bottomBar = checkData(data) ? button : 34 | 35 | return
36 | {!isLoading ? {followersMap}{bottomBar} : } 37 |
; 38 | } 39 | -------------------------------------------------------------------------------- /src/components/follow/followersDiv.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './style.module.scss' 3 | 4 | export default function FollowersDiv({ children }) { 5 | return
6 | {children} 7 |
; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/follow/followingCard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Loading from "./loading"; 3 | import FollowersDiv from "./followersDiv"; 4 | import FollowItem from "./FollowItem"; 5 | import NoUserMessage from "./noUserMessage"; 6 | 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { setFollowingPage } from "@/redux/usersSlice"; 9 | import { useGetGithubUserByFollowingQuery } from "@/services/users"; 10 | 11 | import styles from './style.module.scss'; 12 | 13 | export default function FollowingCard({ nonFollower }) { 14 | 15 | const dispatch = useDispatch(); 16 | const { search, followingPage, following } = useSelector(state => state.users) 17 | const { data, isLoading, isFetching, isSuccess } = useGetGithubUserByFollowingQuery({ name: search, followingPage }, { 18 | refetchOnMountOrArgChange: true, 19 | }); 20 | 21 | const changePageNumber = () => { 22 | if (isSuccess) { 23 | dispatch(setFollowingPage(1)) 24 | } 25 | } 26 | 27 | const checkData = (data) => data?.length != 0 && data?.length == process.env.NEXT_PUBLIC_FOLLOWERS_PER_PAGE 28 | const button = 31 | 32 | const followersMap = following?.map(item => ) 33 | 34 | const bottomBar = checkData(data) ? button : 35 | 36 | return
37 | {!isLoading ? {followersMap}{bottomBar} : } 38 |
; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/follow/index.js: -------------------------------------------------------------------------------- 1 | import FollowerCard from "./followerCard"; 2 | import FollowingCard from "./followingCard"; 3 | import NonFollowersCard from "./nonFollowers"; 4 | 5 | export { FollowerCard, FollowingCard, NonFollowersCard } -------------------------------------------------------------------------------- /src/components/follow/loading.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function Loading() { 4 | return
Loading...
; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/follow/noUserMessage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './style.module.scss'; 3 | 4 | export default function NoUserMessage() { 5 | return
There are no more users to display....
; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/follow/nonFollowers.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import FollowItem from "./FollowItem"; 3 | import { useSelector } from "react-redux"; 4 | import styles from './style.module.scss'; 5 | import { fetchAllFollowersData, fetchAllFollowingData } from "@/helper/getAllData"; 6 | 7 | export default function NonFollowersCard() { 8 | 9 | const { search } = useSelector((state) => state.users); 10 | const [followers, setFollowers] = useState(); 11 | const [following, setFollowing] = useState(); 12 | 13 | useEffect(() => { 14 | fetchAllFollowersData(search).then(data => setFollowers(data)); 15 | fetchAllFollowingData(search).then(data => setFollowing(data)); 16 | }, [search]); 17 | 18 | const nonFollower = following?.filter((user) => { 19 | return !followers?.some((follower) => { 20 | return follower.id === user.id; 21 | }); 22 | }); 23 | 24 | return
25 | {nonFollower?.map((item, id) => )} 26 |
27 | } 28 | -------------------------------------------------------------------------------- /src/components/follow/style.module.scss: -------------------------------------------------------------------------------- 1 | .follow__card { 2 | height: "400px"; 3 | 4 | .follow__item { 5 | display: flex; 6 | gap: 10px; 7 | padding: 10px 5px; 8 | 9 | .avatar__url { 10 | width: 50px; 11 | height: 50px; 12 | border-radius: 50%; 13 | } 14 | 15 | .followers__info { 16 | h4 { 17 | font-weight: bold; 18 | font-size: 1.8rem; 19 | color: hsla(0, 0%, 100%); 20 | } 21 | a { 22 | color: hsla(0, 0%, 100%, 0.8); 23 | font-size: 1.4rem; 24 | } 25 | } 26 | } 27 | 28 | .follow__go_on_button { 29 | background-color: #efefef; 30 | padding: 10px; 31 | width: 100%; 32 | border-radius: 20px; 33 | margin-top: 10px; 34 | } 35 | 36 | .no_user__message { 37 | font-size: 1.3rem; 38 | color: rgb(182, 182, 182); 39 | padding: 15px 5px; 40 | text-align: center; 41 | margin-top: 5px; 42 | border-top: 2px solid white ; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/fusioncharts/FusionChart.js: -------------------------------------------------------------------------------- 1 | import ReactFC from 'react-fusioncharts'; 2 | 3 | import FusionCharts from 'fusioncharts'; 4 | import Column2D from 'fusioncharts/fusioncharts.charts'; 5 | import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; 6 | 7 | import { 8 | languageConfigs, 9 | forkedReposConfigs, 10 | starsConfigs, 11 | starsPerLangConfigs, 12 | } from '@/data/fusion-configs'; 13 | 14 | import { sortData } from '@/helper/sortData'; 15 | import { findLanguageData } from '@/helper/findLanguageData'; 16 | import styles from './styles.module.scss'; 17 | import Title from '../title/title'; 18 | 19 | ReactFC.fcRoot(FusionCharts, Column2D, FusionTheme); 20 | 21 | 22 | 23 | const NextFusionCharts = ({ reposData }) => { 24 | if (reposData) { 25 | let starsData = sortData(reposData, 'stargazers_count'); 26 | starsConfigs.dataSource.data = starsData; 27 | 28 | let forksData = sortData(reposData, 'forks_count'); 29 | forkedReposConfigs.dataSource.data = forksData; 30 | 31 | const langData = findLanguageData(reposData); 32 | languageConfigs.dataSource.data = langData; 33 | 34 | const starsPerLangData = findLanguageData(reposData, 'stargazers_count'); 35 | starsPerLangConfigs.dataSource.data = starsPerLangData; 36 | } 37 | 38 | return ( 39 |
40 |
41 | 42 | <div className={styles.chart}> 43 | <ReactFC {...languageConfigs} /> 44 | <ReactFC {...starsConfigs} /> 45 | <ReactFC {...starsPerLangConfigs} /> 46 | <ReactFC {...forkedReposConfigs} /> 47 | </div> 48 | </div> 49 | </section> 50 | ); 51 | }; 52 | 53 | export default NextFusionCharts; 54 | -------------------------------------------------------------------------------- /src/components/fusioncharts/styles.module.scss: -------------------------------------------------------------------------------- 1 | .chart { 2 | max-width: 100%; 3 | display: grid; 4 | grid-template-columns: minmax(30%, 1fr) minmax(60%, 1fr); 5 | row-gap: 4rem; 6 | column-gap: 2rem; 7 | margin-top: 2rem; 8 | margin-bottom: 10rem; 9 | } 10 | 11 | @media (max-width: 900px) { 12 | .chart { 13 | grid-template-columns: minmax(30%, 1fr); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/search/index.js: -------------------------------------------------------------------------------- 1 | //core 2 | import React, { useRef } from 'react'; 3 | 4 | //styles 5 | import styles from './styles.module.scss'; 6 | 7 | //redux 8 | import { useDispatch } from 'react-redux'; 9 | import { setSearch } from '@/redux/usersSlice'; 10 | 11 | //icons 12 | import { SearchIcon } from '@/icons/index.js'; 13 | 14 | //next 15 | import { useRouter } from 'next/router'; 16 | 17 | 18 | export default function Search(props) { 19 | const dispatch = useDispatch(); 20 | const searchRef = useRef(); 21 | const router = useRouter(); 22 | 23 | const handleSearch = (e) => { 24 | e.preventDefault(); 25 | let searchValue = searchRef.current.value; 26 | if (searchValue.trim()) { 27 | dispatch(setSearch(searchValue.trim())); 28 | router.push('/dashboard'); 29 | } 30 | }; 31 | 32 | return ( 33 | <form onSubmit={handleSearch} className={styles.search}> 34 | <SearchIcon /> 35 | <input 36 | type="text" 37 | placeholder="Search github users..." 38 | ref={searchRef} 39 | className={styles.search__input} 40 | {...props} 41 | /> 42 | </form> 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/components/search/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/_variables"; 2 | 3 | .search { 4 | margin-left: 10px; 5 | margin-right: 10px; 6 | position: relative; 7 | 8 | input { 9 | background-color: $bg-color; 10 | border-radius: 20px; 11 | padding: 10px 20px; 12 | padding-left: 30px; 13 | width: 250px; 14 | height: 40px; 15 | outline: 0; 16 | transition: all 0.3s; 17 | color: rgba(255, 255, 255, 0.8); 18 | 19 | &:focus { 20 | box-shadow: inset 0px 0px 0px 2px #19a7ce; 21 | } 22 | } 23 | svg { 24 | position: absolute; 25 | left: 20px; 26 | top: 50%; 27 | transform: translate(-50%, -50%); 28 | 29 | fill: #fff; 30 | opacity: 0.5; 31 | width: 2rem; 32 | height: 1.5rem; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/stats/StatsCard.js: -------------------------------------------------------------------------------- 1 | //components 2 | import Title from '../title/title'; 3 | import StatsCardItem from './StatsCartdtem'; 4 | 5 | //styles 6 | import styles from './styles.module.scss' 7 | 8 | //icons 9 | import { Repos, Followers, Following, Gists } from '@/icons' 10 | 11 | const StatsCard = ({ data }) => { 12 | return ( 13 | <section> 14 | <div className="container container__inner"> 15 | <Title h1="User Statistics" p="GitHub User Information: Repositories, Followers, Following, and Gists" /> 16 | <div className={styles['stats-container']}> 17 | <StatsCardItem icon={<Repos />} text='repos' num={data?.public_repos} /> 18 | <StatsCardItem style={{ backgroundColor: "#262A56" }} icon={<Followers />} text='followers' num={data?.followers} /> 19 | <StatsCardItem icon={<Following />} text='following' num={data?.following} /> 20 | <StatsCardItem style={{ backgroundColor: "#262A56" }} icon={<Gists />} text='gists' num={data?.public_gists} /> 21 | </div> 22 | 23 | </div> 24 | </section> 25 | ); 26 | }; 27 | 28 | export default StatsCard; 29 | -------------------------------------------------------------------------------- /src/components/stats/StatsCartdtem.js: -------------------------------------------------------------------------------- 1 | //styles 2 | import styles from './styles.module.scss' 3 | 4 | const StatsCardItem = ({ icon, text, num, ...props }) => { 5 | return ( 6 | <article className={styles.stats__card} {...props}> 7 | <div className={styles.stats__inner}> 8 | <span className={`${styles.icon} ${styles[text]}`} > 9 | {icon} 10 | </span> 11 | <div className={styles.info}> 12 | <h3>{num}</h3> 13 | <p >{text}</p> 14 | </div> 15 | </div> 16 | </article> 17 | ) 18 | } 19 | 20 | export default StatsCardItem -------------------------------------------------------------------------------- /src/components/stats/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/_variables"; 2 | 3 | .stats-container { 4 | display: grid; 5 | grid-template-columns: repeat(4, 1fr); 6 | align-items: center; 7 | gap: 2rem; 8 | margin-top: 20px; 9 | margin-bottom: 20px; 10 | } 11 | 12 | .stats__card { 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | height: 132px; 17 | border-radius: 20px; 18 | padding: 1.5rem; 19 | background-color: $bg_color_blue; 20 | 21 | .stats__inner { 22 | display: flex; 23 | align-items: flex-start; 24 | justify-items: center; 25 | } 26 | 27 | .icon { 28 | padding: 10px; 29 | border-radius: 50%; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | 34 | svg { 35 | width: 5rem; 36 | height: 5rem; 37 | color: #fff; 38 | } 39 | } 40 | 41 | .info { 42 | width: 100%; 43 | text-align: center; 44 | color: #fff; 45 | 46 | h3 { 47 | font-style: normal; 48 | font-weight: 700; 49 | font-size: 64px; 50 | line-height: 90.02%; 51 | } 52 | } 53 | } 54 | 55 | @media (max-width: 900px) { 56 | .stats-container { 57 | grid-template-columns: repeat(2, 1fr); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/components/table/table-mobile.js: -------------------------------------------------------------------------------- 1 | import styles from './table-mobile.module.scss'; 2 | 3 | export default function TableMobile({ head, body }) { 4 | return ( 5 | <div className={styles.tableBox}> 6 | {body.map((items, index) => ( 7 | <article key={index}> 8 | {items.map((item, key) => 9 | <div> 10 | <h6>{head[key].name}</h6> 11 | <p>{item}</p> 12 | </div> 13 | )} 14 | </article> 15 | ))} 16 | </div> 17 | ) 18 | } -------------------------------------------------------------------------------- /src/components/table/table-mobile.module.scss: -------------------------------------------------------------------------------- 1 | .tableBox { 2 | font-size: 2.9rem; 3 | 4 | article { 5 | border: 1px solid #f9fafb; 6 | background-color: #f9fafb; 7 | padding: 16px; 8 | border-radius: 10px; 9 | margin-top: 20px; 10 | 11 | div { 12 | padding-top: 10px; 13 | padding-bottom: 10px; 14 | p { 15 | font-size: 2rem; 16 | } 17 | a { 18 | font-size: 2rem; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/table/table.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { FaSortDown, FaSortUp, FaSort } from "react-icons/fa" 3 | import { useMediaQuery } from '@react-hook/media-query' 4 | import TableMobile from "./table-mobile"; 5 | import styles from './table.module.scss'; 6 | 7 | 8 | export default function Table({ head, body, searchable }) { 9 | 10 | const isMobile = useMediaQuery('(max-width: 600px)') 11 | 12 | const [sorting, setSorting] = useState(false) 13 | const [search, setSearch] = useState('') 14 | const filteredData = body && body.filter( 15 | items => items.some( 16 | item => ((item?.key || item?.props?.searchableText || item) || (item !== "null")).toString().toLocaleLowerCase('TR').includes(search.toLocaleLowerCase('TR')) 17 | ) 18 | ).sort((a, b) => { 19 | if (sorting?.orderBy === 'asc') { 20 | if (typeof b[sorting.key] === "number") { 21 | return a[sorting.key] - b[sorting.key]; 22 | } 23 | return (a[sorting.key]?.key || a[sorting.key]?.props?.searchableText || a[sorting.key]).toString().localeCompare(b[sorting.key]?.key || b[sorting.key]?.props?.searchableText || b[sorting.key]) 24 | } 25 | if (sorting?.orderBy === 'desc') { 26 | if (typeof b[sorting.key] === "number") { 27 | return b[sorting.key] - a[sorting.key]; 28 | } 29 | return b[sorting.key].toString().localeCompare(a[sorting.key]) 30 | } 31 | }) 32 | 33 | if (!body || body?.length === 0) { 34 | return ( 35 | <div className={styles["no-data"]}>Gösterilecek veri bulunmuyor.</div> 36 | ) 37 | } 38 | 39 | return ( 40 | <> 41 | {searchable && ( 42 | <div className={styles.searchable}> 43 | <input 44 | value={search} 45 | onChange={e => setSearch(e.target.value)} 46 | type="text" 47 | placeholder="Tabloda ara" 48 | /> 49 | {sorting && ( 50 | <button 51 | onClick={() => setSorting(false)} 52 | > 53 | Sıralamayı İptal Et 54 | </button> 55 | )} 56 | </div> 57 | )} 58 | {isMobile && <TableMobile head={head} body={filteredData} />} 59 | {!isMobile && ( 60 | <div className={styles.table__container}> 61 | <table> 62 | <thead> 63 | <tr> 64 | {head.map((h, key) => ( 65 | <th 66 | width={h?.width} 67 | key={key}> 68 | <div> 69 | {h.name} 70 | {h.sortable && ( 71 | <button onClick={() => { 72 | if (sorting?.key === key) { 73 | setSorting({ 74 | key, 75 | orderBy: sorting.orderBy === 'asc' ? 'desc' : 'asc' 76 | }) 77 | } else { 78 | setSorting({ 79 | key, 80 | orderBy: 'asc' 81 | }) 82 | } 83 | }}> 84 | {sorting?.key === key && ( 85 | sorting.orderBy === 'asc' ? <FaSortDown size={14} /> : <FaSortUp size={14} /> 86 | )} 87 | {sorting?.key !== key && <FaSort size={14} />} 88 | </button> 89 | )} 90 | </div> 91 | </th> 92 | ))} 93 | </tr> 94 | </thead> 95 | <tbody> 96 | {filteredData.map((items, key) => ( 97 | <tr key={key}> 98 | {items.map((item, key) => ( 99 | <td 100 | key={key}> 101 | {Array.isArray(item) ? ( 102 | <div> 103 | {item} 104 | </div> 105 | ) : item} 106 | </td> 107 | ))} 108 | </tr> 109 | ))} 110 | </tbody> 111 | </table> 112 | </div > 113 | ) 114 | } 115 | </> 116 | ) 117 | } -------------------------------------------------------------------------------- /src/components/table/table.module.scss: -------------------------------------------------------------------------------- 1 | .no-data { 2 | padding: 1rem; 3 | background-color: #fef3c7; 4 | color: #b45309; 5 | font-size: 1.5rem; 6 | line-height: 1.5rem; 7 | border-radius: 10px; 8 | } 9 | 10 | .searchable { 11 | display: flex; 12 | margin-bottom: 1rem; 13 | column-gap: 0.5rem; 14 | margin-top: 5rem; 15 | 16 | input { 17 | padding: 1.8rem 1.8rem; 18 | font-size: 1.2rem; 19 | line-height: 1.25rem; 20 | width: 100%; 21 | border-radius: 10px; 22 | border-width: 1px; 23 | border-color: #d1d5db; 24 | outline: 0; 25 | } 26 | 27 | button { 28 | height: 100%; 29 | padding: 1.8rem 1.8rem; 30 | color: #000000; 31 | font-size: 1.4rem; 32 | line-height: 1.25rem; 33 | white-space: nowrap; 34 | border-radius: 10px; 35 | border-color: #ffffff; 36 | cursor: pointer; 37 | background-color: #fff; 38 | } 39 | } 40 | 41 | .table__container { 42 | width: 100%; 43 | border-radius: 0.25rem; 44 | border-width: 1px; 45 | font-size: 1.4rem; 46 | margin-top: 2rem; 47 | color: #fff; 48 | table { 49 | width: 100%; 50 | border-collapse: collapse; 51 | } 52 | 53 | th { 54 | margin: 5px; 55 | padding: 1rem; 56 | background-color: #343f50; 57 | color: #fff; 58 | line-height: 1.25rem; 59 | font-weight: 600; 60 | text-align: left; 61 | border-bottom-width: 1px; 62 | border-top-left-radius: 10px; 63 | border-top-right-radius: 10px; 64 | margin-right: 10px; 65 | border: 1px solid #000000; 66 | 67 | div { 68 | width: 100%; 69 | display: inline-flex; 70 | justify-content: space-between; 71 | align-items: center; 72 | column-gap: 0.5rem; 73 | } 74 | } 75 | tbody { 76 | tr { 77 | background-color: transparent; 78 | transition: all 0.05s; 79 | &:hover { 80 | background-color: #343f50; 81 | } 82 | 83 | &:last-child { 84 | td { 85 | border-bottom-left-radius: 10px; 86 | border-bottom-right-radius: 10px; 87 | } 88 | } 89 | 90 | td { 91 | padding: 1rem; 92 | line-height: 2rem; 93 | border: 1px solid #343f50; 94 | div { 95 | display: flex; 96 | column-gap: 0.625rem; 97 | } 98 | 99 | a { 100 | color: #fff; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/components/title/styles.module.scss: -------------------------------------------------------------------------------- 1 | .title { 2 | color: #fff; 3 | margin-top: 5rem; 4 | h2 { 5 | font-size: 2rem; 6 | } 7 | p { 8 | font-size: 1.6rem; 9 | color: hsla(0, 0%, 100%, 0.5); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/components/title/title.js: -------------------------------------------------------------------------------- 1 | import styles from './styles.module.scss'; 2 | 3 | export default function Title({ h1, p }) { 4 | return <div className={styles.title}> 5 | <h2>{h1}</h2> 6 | <p>{p}</p> 7 | </div>; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/userCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.scss'; 3 | import { Company, LinkIcon, Location } from '@/icons'; 4 | 5 | const UserCard = ({ data }) => { 6 | 7 | const showUserInfo = (name, IconComponent) => name && <p> <IconComponent /> {name} </p> 8 | 9 | return ( 10 | <div className={`${styles.card}`}> 11 | <div className={styles.user}> 12 | <article> 13 | <header> 14 | <img src={data?.avatar_url} alt={data?.name} /> 15 | <div> 16 | <h4>{data?.name}</h4> 17 | <p>@{data?.login}</p> 18 | </div> 19 | <a 20 | className={styles['follow-link']} 21 | href={data?.html_url} 22 | target="_blank" 23 | > 24 | follow 25 | </a> 26 | </header> 27 | <p className={styles.bio}>{data?.bio}</p> 28 | <div className={styles['user-info']}> 29 | {showUserInfo(data?.company, Company)} 30 | {showUserInfo(data?.location, Location)} 31 | {showUserInfo(data?.blog, LinkIcon)} 32 | </div> 33 | </article> 34 | </div> 35 | </div> 36 | ); 37 | }; 38 | 39 | export default UserCard; 40 | -------------------------------------------------------------------------------- /src/components/userCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/variables"; 2 | 3 | .card { 4 | flex: 1; 5 | background-color: #19376d; 6 | border-radius: 20px; 7 | position: relative; 8 | color: #fff; 9 | } 10 | 11 | .user { 12 | padding: 3rem; 13 | header { 14 | display: flex; 15 | gap: 1.5rem; 16 | align-items: center; 17 | margin-bottom: 2rem; 18 | 19 | img { 20 | width: 9rem; 21 | height: 9rem; 22 | border-radius: 50%; 23 | object-fit: cover; 24 | } 25 | 26 | div { 27 | margin-right: auto; 28 | 29 | h4 { 30 | font-size: 2rem; 31 | letter-spacing: 1.2px; 32 | } 33 | 34 | p { 35 | font-size: 1.4rem; 36 | color: #fff; 37 | } 38 | } 39 | } 40 | 41 | .bio { 42 | margin-bottom: 3rem; 43 | } 44 | 45 | .follow-link { 46 | background: #d9d9d9; 47 | border-radius: 75px; 48 | padding: 10px 30px; 49 | color: #000; 50 | } 51 | 52 | .user-info { 53 | p, 54 | a { 55 | font-size: 1.6rem; 56 | color: rgba(255, 255, 255, 0.815); 57 | margin-bottom: 10px; 58 | 59 | svg { 60 | margin-right: 8px; 61 | } 62 | } 63 | } 64 | } 65 | 66 | @media (max-width: 900px) { 67 | .card { 68 | margin-bottom: 4rem; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/container/dashboard/followerCardContainer/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FollowerCard } from "@/components/follow"; 3 | import styles from '@/container/dashboard/styles.module.scss'; 4 | import Title from "@/components/title/title"; 5 | import UserCard from "@/components/userCard"; 6 | 7 | export default function FollowerCardContainer({ data }) { 8 | return <section style={{ marginBottom: "2rem" }}> 9 | <div className="container"> 10 | <div className={styles.cards__container}> 11 | <div> 12 | <Title h1="User Bio" p="Information about the user" /> 13 | <div style={{ marginTop: "20px", height: "400px", backgroundColor: "#19376d", borderRadius: "20px", padding: " 20px" }}> 14 | <UserCard data={data} /> 15 | </div> 16 | </div> 17 | <div> 18 | <Title h1="Follower" p="Users who follow" /> 19 | <div className={styles.follower__container}> 20 | <FollowerCard /> 21 | </div> 22 | </div> 23 | </div> 24 | </div> 25 | </section > 26 | } -------------------------------------------------------------------------------- /src/container/dashboard/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import dynamic from 'next/dynamic'; 3 | import StatsCard from '@/components/stats/StatsCard'; 4 | const NextFusionCharts = dynamic( 5 | () => import('@/components/fusioncharts/FusionChart'), 6 | { ssr: false } 7 | ); 8 | import { useGetGithubUserByNameQuery } from '@/services/users'; 9 | import { useSelector } from 'react-redux'; 10 | import FollowerCardContainer from './followerCardContainer'; 11 | import { fetchAllReposData } from '@/helper/getAllData'; 12 | 13 | export default function DashBoardContainer() { 14 | 15 | const [repos, setRepos] = useState(); 16 | const { search } = useSelector((state) => state.users); 17 | const { data, isLoading } = useGetGithubUserByNameQuery(search); 18 | 19 | 20 | useEffect(() => { 21 | fetchAllReposData(search).then(data => setRepos(data)); 22 | }, [search]); 23 | 24 | 25 | return ( 26 | <> 27 | {!isLoading && <main> 28 | <StatsCard data={data} /> 29 | <FollowerCardContainer data={data} /> 30 | <NextFusionCharts reposData={repos} /> 31 | </main>} 32 | </> 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/container/dashboard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .cards__container { 2 | display: grid; 3 | grid-template-columns: repeat(2, 1fr); 4 | gap: 2rem; 5 | } 6 | 7 | .follower__container { 8 | margin-top: 20px; 9 | height: 400px; 10 | overflow: auto; 11 | background-color: #19376d; 12 | border-radius: 20px; 13 | padding: 20px; 14 | overscroll-behavior: contain; 15 | 16 | /* width */ 17 | &::-webkit-scrollbar { 18 | width: 8px; 19 | border-radius: 50%; 20 | } 21 | 22 | /* Track */ 23 | &::-webkit-scrollbar-track { 24 | background: #efefef; 25 | border-radius: 20px; 26 | padding: 1px; 27 | } 28 | 29 | /* Handle */ 30 | &::-webkit-scrollbar-thumb { 31 | background: #333; 32 | } 33 | 34 | /* Handle on hover */ 35 | &::-webkit-scrollbar-thumb:hover { 36 | background: #111; 37 | } 38 | } 39 | 40 | @media only screen and (max-width: 700px) { 41 | .cards__container { 42 | grid-template-columns: repeat(1, 1fr); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/container/home/about/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './styles.module.scss'; 3 | 4 | export default function About() { 5 | return <> 6 | <section className={styles.about__section}> 7 | <div className="container"> 8 | <div className={styles.container__inner}> 9 | <h2>About Github Insight</h2> 10 | <p>Github Insight is an application that uses Github API to analyze user data on Github accounts. This application allows users to view statistics on their Github repositories, follower count, followed users, non-followers, rate limits, and many other pieces of information.</p> 11 | </div> 12 | </div> 13 | </section> 14 | </>; 15 | } 16 | -------------------------------------------------------------------------------- /src/container/home/about/styles.module.scss: -------------------------------------------------------------------------------- 1 | .about__section { 2 | background-color: #010101; 3 | padding-top: 100px; 4 | padding-bottom: 100px; 5 | 6 | .container__inner { 7 | width: 600px; 8 | margin-left: auto; 9 | margin-right: auto; 10 | } 11 | 12 | h2, 13 | p { 14 | text-align: center; 15 | color: #ffffffd7; 16 | } 17 | 18 | h2 { 19 | font-weight: 700; 20 | font-size: 32px; 21 | line-height: 222.52%; 22 | } 23 | 24 | p { 25 | font-weight: 400; 26 | font-size: 16px; 27 | line-height: 222.52%; 28 | } 29 | } 30 | 31 | @media only screen and (max-width: 700px) { 32 | .container__inner { 33 | width: 100% !important; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/container/home/feature/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './styles.module.scss'; 3 | import { NonFollowersIcon, RateIcon, Repos } from "@/icons"; 4 | 5 | import Link from 'next/link'; 6 | 7 | export default function Feature() { 8 | return <section> 9 | <div className="container"> 10 | <div className={styles.container_inner}> 11 | <div className={styles.container__inner__box}> 12 | <div className={styles.image}> 13 | <Repos style={{ width: "36px", height: "36px" }} /> 14 | </div> 15 | <div className={styles.text}> 16 | <h2>Repos</h2> 17 | <p>The repositories pulled from GitHub are collections of code and related files that developers use to manage and collaborate on their projects. </p> 18 | <Link href="/repos">Analyze</Link> 19 | </div> 20 | </div> 21 | <div className={styles.container__inner__box}> 22 | <div className={styles.image}> 23 | <NonFollowersIcon style={{ width: "36px", height: "36px" }} /> 24 | </div> 25 | <div className={styles.text}> 26 | <h2>Non-followers</h2> 27 | <p>The non-followers feature allows you to identify users who do not follow you back on GitHub. This feature can help you manage your follower list and identify potential followers who may be interested in your work.</p> 28 | <Link href="/non-followers">Analyze</Link> 29 | </div> 30 | </div> 31 | <div className={styles.container__inner__box}> 32 | <div className={styles.image}> 33 | <RateIcon style={{ width: "36px", height: "36px" }} /> 34 | </div> 35 | <div className={styles.text}> 36 | <h2>Rate</h2> 37 | <p>The rate feature enables you to analyze users' activities on GitHub, such as their repositories, stars, forks, and other statistics. This feature can help you gauge a user's level of activity and engagement on the platform.</p> 38 | <Link href="rate">Analyze</Link> 39 | </div> 40 | </div> 41 | </div> 42 | </div> 43 | </section >; 44 | } 45 | -------------------------------------------------------------------------------- /src/container/home/feature/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container_inner { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | padding-top: 40px; 7 | padding-bottom: 40px; 8 | 9 | .container__inner__box { 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | width: 600px; 14 | gap: 20px; 15 | margin-bottom: 60px; 16 | 17 | &:nth-child(2n) { 18 | .image { 19 | order: 1; 20 | } 21 | } 22 | } 23 | 24 | .image { 25 | border-radius: 50%; 26 | max-width: 120px; 27 | max-height: 120px; 28 | min-width: 120px; 29 | min-height: 120px; 30 | background-color: #fff; 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | } 35 | 36 | .text { 37 | font-weight: 400; 38 | font-size: 16px; 39 | line-height: 160.02%; 40 | color: #ffffff; 41 | h2 { 42 | font-weight: 700; 43 | font-size: 20px; 44 | line-height: 222.52%; 45 | } 46 | } 47 | } 48 | 49 | @media only screen and (max-width: 700px) { 50 | .container__inner__box { 51 | flex-direction: column; 52 | width: 100% !important; 53 | text-align: center; 54 | 55 | &:nth-child(2n) { 56 | .image { 57 | order: 0 !important; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/container/home/hero/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './styles.module.scss'; 3 | import blockChart from '@/assets/block-chart.png' 4 | import chart from '@/assets/chart.png' 5 | import greenOk from '@/assets/green-ok.png' 6 | import okDown from '@/assets/ok-down.png' 7 | import Search from "@/components/search"; 8 | 9 | export default function Hero() { 10 | 11 | console.log(blockChart.src); 12 | 13 | return <> 14 | <section> 15 | <div className="container"> 16 | <div className={styles.hero_container}> 17 | <div className={styles.hero_container_inner}> 18 | <img src={blockChart.src} className={styles.blockChart} /> 19 | <img src={chart.src} className={styles.chart} /> 20 | <img src={greenOk.src} className={styles.greenOk} /> 21 | <img src={okDown.src} className={styles.okDown} /> 22 | <h1>Explore Your GitHub</h1> 23 | <Search style={{ 24 | width: "100%", border: "1px solid #fff" 25 | }} /> 26 | < p > <b>Analyze</b>, < b > Refine</b>, and <b>Boost</b> <br /> Your Github Account.</p> 27 | </div> 28 | </div> 29 | </div > 30 | </section > 31 | </>; 32 | } 33 | -------------------------------------------------------------------------------- /src/container/home/hero/styles.module.scss: -------------------------------------------------------------------------------- 1 | .hero_container { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: calc(100vh - 100px); 6 | 7 | .hero_container_inner { 8 | position: relative; 9 | width: 600px; 10 | } 11 | 12 | h1, 13 | p { 14 | text-align: center; 15 | } 16 | 17 | h1 { 18 | font-weight: 700; 19 | font-size: 64px; 20 | line-height: 77px; 21 | color: rgba(138, 144, 200, 0.86); 22 | } 23 | 24 | p { 25 | font-weight: 300; 26 | font-size: 36px; 27 | line-height: 70px; 28 | text-align: center; 29 | color: #ffffff; 30 | } 31 | 32 | .blockChart, 33 | .chart, 34 | .greenOk, 35 | .okDown { 36 | position: absolute; 37 | } 38 | 39 | .blockChart { 40 | right: -50px; 41 | top: -60px; 42 | } 43 | 44 | .chart { 45 | left: 100px; 46 | bottom: -50px; 47 | } 48 | 49 | .greenOk { 50 | left: -100px; 51 | top: -50px; 52 | } 53 | 54 | .okDown { 55 | right: 0px; 56 | bottom: -100px; 57 | } 58 | } 59 | 60 | @media only screen and (max-width: 700px) { 61 | .blockChart, 62 | .chart, 63 | .greenOk, 64 | .okDown { 65 | display: none; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/container/home/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Hero from "./hero"; 3 | import About from "./about"; 4 | import Feature from "./feature"; 5 | import Footer from "@/layout/footer"; 6 | 7 | export default function HomeContainer() { 8 | return (<> 9 | <Hero /> 10 | <About /> 11 | <Feature /> 12 | </>); 13 | } 14 | -------------------------------------------------------------------------------- /src/container/home/styles.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasinelbuz/github-insight/fe8b78052cc4e2b24ee9c7b243c6cc410574b452/src/container/home/styles.module.scss -------------------------------------------------------------------------------- /src/container/nonFollowers/container/Follower.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from '@/container/nonFollowers/nonFollowers.module.scss'; 3 | const Follower = ({ followers }) => { 4 | return ( 5 | <article className={styles.box}> 6 | <img 7 | src={followers.avatar_url} 8 | alt={styles.login} 9 | className={styles.avatar__url} 10 | /> 11 | <div className={styles.followers__info}> 12 | <h4>{followers?.login}</h4> 13 | <a 14 | className={styles.follow__link} 15 | href={followers.html_url} 16 | target="_blank" 17 | > 18 | {followers.html_url} 19 | </a> 20 | </div> 21 | </article> 22 | ); 23 | }; 24 | 25 | export default Follower; 26 | -------------------------------------------------------------------------------- /src/container/nonFollowers/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './nonFollowers.module.scss' 3 | import { FollowerCard, FollowingCard, NonFollowersCard } from '@/components/follow/' 4 | 5 | export default function NonFollowersContainer() { 6 | return <main> 7 | <section className={styles.section_container}> 8 | <div className="container"> 9 | <h1>Non-Followers</h1> 10 | <p>Github Follower Analysis: This page shows follower and following statistics related to your Github account. The list you see on the page consists of non-followers, which are obtained by taking the difference between your followers and following lists.</p> 11 | <section> 12 | <div className={styles.nonfollowers__container}> 13 | 14 | <div className={styles.follower}> 15 | <h2>Follower</h2> 16 | <div> 17 | <FollowerCard /> 18 | </div> 19 | </div> 20 | 21 | <div className={styles.following}> 22 | <h2>Following</h2> 23 | <FollowingCard /> 24 | </div> 25 | 26 | <div className={styles.nonfollowers}> 27 | <h2>Non-Followers</h2> 28 | <NonFollowersCard /> 29 | </div> 30 | </div> 31 | </section> 32 | </div> 33 | </section> 34 | </main> 35 | } 36 | -------------------------------------------------------------------------------- /src/container/nonFollowers/nonFollowers.module.scss: -------------------------------------------------------------------------------- 1 | .nonfollowers__container { 2 | display: grid; 3 | grid-template-columns: repeat(3, 1fr); 4 | align-items: center; 5 | grid-gap: 2rem; 6 | gap: 2rem; 7 | margin-top: 5rem; 8 | 9 | & > div { 10 | background-color: #161b22; 11 | padding: 10px; 12 | border-radius: 10px; 13 | height: 400px; 14 | overflow-y: auto; 15 | } 16 | } 17 | 18 | .box { 19 | display: flex; 20 | } 21 | 22 | .avatar__url { 23 | width: 50px; 24 | height: 50px; 25 | border-radius: 50%; 26 | } 27 | 28 | .section_container { 29 | margin-top: 5rem; 30 | margin-bottom: 5rem; 31 | h1, 32 | p { 33 | color: #fff; 34 | text-align: center; 35 | width: 800px; 36 | margin-left: auto; 37 | margin-right: auto; 38 | line-height: 2.6rem; 39 | } 40 | h1 { 41 | font-size: 2.8rem; 42 | margin-bottom: 1rem; 43 | } 44 | } 45 | 46 | .follower { 47 | position: relative; 48 | h2 { 49 | position: sticky; 50 | top: -10px; 51 | padding: 20px; 52 | margin-left: -10px; 53 | margin-top: -10px; 54 | width: calc(100% + 10px); 55 | color: #fff; 56 | background: rgb(0, 0, 0); 57 | background: linear-gradient(180deg, rgba(0, 0, 0, 0.6979166666666667) 75%, rgba(255, 255, 255, 0) 100%); 58 | } 59 | } 60 | 61 | .following { 62 | position: relative; 63 | h2 { 64 | position: sticky; 65 | top: -10px; 66 | padding: 20px; 67 | margin-left: -10px; 68 | margin-top: -10px; 69 | width: calc(100% + 10px); 70 | color: #fff; 71 | background: rgb(0, 0, 0); 72 | background: linear-gradient(180deg, rgba(0, 0, 0, 0.6979166666666667) 75%, rgba(255, 255, 255, 0) 100%); 73 | } 74 | } 75 | 76 | .nonfollowers { 77 | position: relative; 78 | h2 { 79 | position: sticky; 80 | top: -10px; 81 | padding: 20px; 82 | margin-left: -10px; 83 | margin-top: -10px; 84 | width: calc(100% + 10px); 85 | color: #fff; 86 | background: rgb(0, 0, 0); 87 | background: linear-gradient(180deg, rgba(0, 0, 0, 0.6979166666666667) 75%, rgba(255, 255, 255, 0) 100%); 88 | } 89 | } 90 | 91 | @media only screen and (max-width: 900px) { 92 | .nonfollowers__container { 93 | grid-template-columns: repeat(1, 1fr); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/container/rate/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function RateContainer() { 4 | return <section> 5 | <div className="container" style={{ height: "500px", paddingTop: "100px", color: "#fff", fontSize: "5rem" }}> 6 | çok yakında 7 | </div> 8 | </section>; 9 | } 10 | -------------------------------------------------------------------------------- /src/container/repos/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Table from "@/components/table/table"; 3 | import { useGetGithubUserByReposQuery } from "@/services/users"; 4 | import { useSelector } from "react-redux"; 5 | 6 | export default function ReposContainer() { 7 | 8 | const { search } = useSelector(state => state.users); 9 | const { data: repos, isLoading } = useGetGithubUserByReposQuery(search); 10 | 11 | const tableHead = [ 12 | { name: 'Repo name', sortable: true, width: "200px" }, 13 | { name: 'Description' }, 14 | { name: 'Star count', sortable: true }, 15 | { name: 'Forks count', sortable: true }, 16 | { name: 'Size', sortable: true }, 17 | { name: 'Is Fork?', sortable: true }, 18 | { name: 'Created at', sortable: true, width: "125px" }, 19 | { name: 'Repo url' }, 20 | ]; 21 | 22 | const tableBody = repos?.map(repo => ([ 23 | <a href={repo.html_url} style={{ color: "#fff", textDecoration: "underline" }} target="_blank">{repo.name}</a>, 24 | repo.description, 25 | repo.stargazers_count, 26 | repo.forks_count, 27 | repo.size, 28 | repo.fork ? "Yes" : "No", 29 | repo.created_at.slice(0, 10), 30 | <a href={repo.html_url} style={{ color: "#fff", textDecoration: "underline" }} target="_blank">Repo</a>, 31 | ])); 32 | 33 | return <> 34 | <main> 35 | <section> 36 | <div className="container"> 37 | {!isLoading ? <Table 38 | searchable={true} 39 | head={tableHead} 40 | body={repos && tableBody} 41 | /> : "Loading"} 42 | </div> 43 | </section> 44 | </main> 45 | </> 46 | } 47 | -------------------------------------------------------------------------------- /src/data/fusion-configs.js: -------------------------------------------------------------------------------- 1 | const color = '#2caeba, #5d62b5, #ffc533, #f2726f, #8d6e63'; 2 | export const languageConfigs = { 3 | type: 'pie2d', 4 | width: '100%', 5 | height: '400', 6 | dataFormat: 'json', 7 | dataSource: { 8 | chart: { 9 | caption: 'Language', 10 | subCaption: "Which language has more repositories?", 11 | showLegend: '0', 12 | decimals: '0', 13 | showPercentValues: '0', 14 | theme: 'fusion', 15 | palettecolors: color, 16 | "bgColor": "#161B22", 17 | "baseFontColor": "#fff", 18 | "outCnvBaseFontColor": "#fff", 19 | "valueFontColor": "#fff", 20 | "baseFontColor": "#000" 21 | }, 22 | }, 23 | }; 24 | export const starsConfigs = { 25 | type: 'column2d', 26 | width: '100%', 27 | height: '400', 28 | dataFormat: 'json', 29 | dataSource: { 30 | chart: { 31 | caption: 'Most Popular', 32 | subCaption: "Which repos got the most stars?", 33 | showValues: '1', 34 | xAxisName: 'Repos', 35 | yAxisName: 'Stars', 36 | xAxisNameFontSize: '18', 37 | xAxisNameFontBold: '1', 38 | xAxisNameFontColor: '#617d98', 39 | yAxisNameFontSize: '18', 40 | yAxisNameFontBold: '1', 41 | yAxisNameFontColor: '#617d98', 42 | theme: 'fusion', 43 | palettecolors: color, 44 | "bgColor": "#161B22", 45 | "baseFontColor": "#fff", 46 | "outCnvBaseFontColor": "#fff", 47 | "valueFontColor": "#fff", 48 | "baseFontColor": "#000" 49 | }, 50 | }, 51 | }; 52 | export const starsPerLangConfigs = { 53 | type: 'doughnut2d', 54 | width: '100%', 55 | height: '400', 56 | dataFormat: 'json', 57 | dataSource: { 58 | chart: { 59 | caption: 'Stars Per Language', 60 | subCaption: "Which language got more stars?", 61 | decimals: '0', 62 | showPercentValues: '0', 63 | showLegend: '0', 64 | xAxisName: 'Repos', 65 | yAxisName: 'Stars', 66 | theme: 'fusion', 67 | palettecolors: color, 68 | "bgColor": "#161B22", 69 | "baseFontColor": "#fff", 70 | "outCnvBaseFontColor": "#fff", 71 | "valueFontColor": "#fff", 72 | "baseFontColor": "#000" 73 | }, 74 | }, 75 | }; 76 | export const forkedReposConfigs = { 77 | type: 'bar2d', 78 | width: '100%', 79 | height: '400', 80 | dataFormat: 'json', 81 | dataSource: { 82 | chart: { 83 | caption: 'Most Forked', 84 | subCaption: "Which repos are more forked?", 85 | showValues: '1', 86 | xAxisName: 'Repos', 87 | yAxisName: 'Forks', 88 | xAxisNameFontSize: '18', 89 | xAxisNameFontBold: '1', 90 | xAxisNameFontColor: '#617d98', 91 | yAxisNameFontSize: '18', 92 | yAxisNameFontBold: '1', 93 | yAxisNameFontColor: '#fff', 94 | theme: 'fusion', 95 | palettecolors: color, 96 | "bgColor": "#161B22", 97 | "baseFontColor": "#fff", 98 | "outCnvBaseFontColor": "#fff", 99 | "valueFontColor": "#fff", 100 | "baseFontColor": "#000" 101 | 102 | }, 103 | }, 104 | }; -------------------------------------------------------------------------------- /src/data/stats-card-item-data.js: -------------------------------------------------------------------------------- 1 | export const statsCardItemData = [ 2 | { 3 | icon: "Repos", 4 | text: "repos", 5 | num: "public_repos", 6 | }, 7 | { 8 | icon: "Repos", 9 | text: "repos", 10 | num: "public_repos", 11 | }, 12 | { 13 | icon: "Repos", 14 | text: "repos", 15 | num: "public_repos", 16 | }, 17 | { 18 | icon: "Repos", 19 | text: "repos", 20 | num: "public_repos", 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /src/helper/findLanguageData.js: -------------------------------------------------------------------------------- 1 | export const findLanguageData = (data, value) => { 2 | const langObj = data.reduce((acc, cur) => { 3 | if (cur.language !== null) { 4 | if (value) { 5 | acc[cur.language] = (acc[cur.language] || 0) + cur[value]; 6 | } else { 7 | acc[cur.language] = (acc[cur.language] || 0) + 1; 8 | } 9 | } 10 | return acc; 11 | }, {}); 12 | 13 | return Object.keys(langObj).map((language) => { 14 | return { 15 | label: language, 16 | value: langObj[language], 17 | }; 18 | }); 19 | }; -------------------------------------------------------------------------------- /src/helper/getAllData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {*} name search 4 | * @returns 5 | */ 6 | async function fetchAllFollowersData(name) { 7 | let page = 1; 8 | let allData = []; 9 | 10 | for (let index = 1; index <= 3; index++) { 11 | //const response = await fetch(`${process.env.NEXT_PUBLIC_MOCK_API}/followers`) => you wanna mock api 12 | const response = await fetch(`${process.env.NEXT_PUBLIC_API}users/${name}/followers?page=${page}&per_page=${process.env.NEXT_PUBLIC_FOLLOWERS_PER_PAGE}`) 13 | const data = await response.json(); 14 | 15 | if (response.length === 0) { 16 | break; 17 | } 18 | 19 | allData.push(...data); 20 | page++; 21 | } 22 | 23 | return allData; 24 | } 25 | 26 | /** 27 | * 28 | * @param {*} name search 29 | * @returns 30 | */ 31 | async function fetchAllFollowingData(name) { 32 | let page = 1; 33 | let allData = []; 34 | 35 | for (let index = 1; index <= 3; index++) { 36 | //const response = await fetch(`${process.env.NEXT_PUBLIC_MOCK_API}/following`) => you wanna mock api 37 | const response = await fetch(`${process.env.NEXT_PUBLIC_API}users/${name}/following?page=${page}&per_page=${process.env.NEXT_PUBLIC_FOLLOWING_PER_PAGE}`) 38 | const data = await response.json(); 39 | 40 | if (data.length === 0) { 41 | break; 42 | } 43 | 44 | allData.push(...data); 45 | page++; 46 | } 47 | 48 | return allData; 49 | } 50 | 51 | /** 52 | * 53 | * @param {*} name search 54 | * @returns 55 | */ 56 | async function fetchAllReposData(name) { 57 | let page = 1; 58 | let allData = []; 59 | 60 | for (let index = 1; index <= 3; index++) { 61 | //const response = await fetch(`${process.env.NEXT_PUBLIC_MOCK_API}/repos`) => you wanna mock api 62 | const response = await fetch(`${process.env.NEXT_PUBLIC_API}users/${name}/repos?page=${page}&per_page=${process.env.NEXT_PUBLIC_REPOS_PER_PAGE}`) 63 | const data = await response.json(); 64 | 65 | if (data.length === 0) { 66 | break; 67 | } 68 | 69 | allData.push(...data); 70 | page++; 71 | } 72 | 73 | return allData; 74 | } 75 | 76 | export { fetchAllFollowersData, fetchAllFollowingData, fetchAllReposData }; -------------------------------------------------------------------------------- /src/helper/sortData.js: -------------------------------------------------------------------------------- 1 | export const sortData = (data, text) => { 2 | return [...data] 3 | ?.sort((a, b) => { 4 | return b[text] - a[text]; 5 | }) 6 | .slice(0, 5) 7 | .map((item) => ({ 8 | label: item.name, 9 | value: item[text], 10 | })); 11 | }; 12 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | export const Repos = (props) => { 2 | return ( 3 | <svg 4 | stroke="currentColor" 5 | fill="currentColor" 6 | strokeWidth="0" 7 | viewBox="0 0 12 16" 8 | className="icon" 9 | height="1em" 10 | width="1em" 11 | xmlns="http://www.w3.org/2000/svg" 12 | {...props} 13 | > 14 | <path 15 | fillRule="evenodd" 16 | d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z" 17 | ></path> 18 | </svg> 19 | ); 20 | }; 21 | 22 | export const Followers = (props) => { 23 | return ( 24 | <svg 25 | stroke="currentColor" 26 | fill="none" 27 | strokeWidth="2" 28 | viewBox="0 0 24 24" 29 | strokeLinecap="round" 30 | strokeLinejoin="round" 31 | className="icon" 32 | height="1em" 33 | width="1em" 34 | xmlns="http://www.w3.org/2000/svg" 35 | {...props} 36 | > 37 | <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> 38 | <circle cx="9" cy="7" r="4"></circle> 39 | <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> 40 | <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> 41 | </svg> 42 | ); 43 | }; 44 | 45 | export const Following = (props) => { 46 | return ( 47 | <svg 48 | stroke="currentColor" 49 | fill="none" 50 | strokeWidth="2" 51 | viewBox="0 0 24 24" 52 | strokeLinecap="round" 53 | strokeLinejoin="round" 54 | className="icon" 55 | height="1em" 56 | width="1em" 57 | xmlns="http://www.w3.org/2000/svg" 58 | {...props} 59 | > 60 | <path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> 61 | <circle cx="8.5" cy="7" r="4"></circle> 62 | <line x1="20" y1="8" x2="20" y2="14"></line> 63 | <line x1="23" y1="11" x2="17" y2="11"></line> 64 | </svg> 65 | ); 66 | }; 67 | export const Gists = (props) => { 68 | return ( 69 | <svg 70 | stroke="currentColor" 71 | fill="currentColor" 72 | strokeWidth="0" 73 | viewBox="0 0 12 16" 74 | className="icon" 75 | height="1em" 76 | width="1em" 77 | xmlns="http://www.w3.org/2000/svg" 78 | {...props} 79 | > 80 | <path 81 | fillRule="evenodd" 82 | d="M7.5 5L10 7.5 7.5 10l-.75-.75L8.5 7.5 6.75 5.75 7.5 5zm-3 0L2 7.5 4.5 10l.75-.75L3.5 7.5l1.75-1.75L4.5 5zM0 13V2c0-.55.45-1 1-1h10c.55 0 1 .45 1 1v11c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1zm1 0h10V2H1v11z" 83 | ></path> 84 | </svg> 85 | ); 86 | }; 87 | export const Location = (props) => { 88 | return ( 89 | <svg 90 | stroke="currentColor" 91 | fill="currentColor" 92 | strokeWidth="0" 93 | viewBox="0 0 24 24" 94 | height="1em" 95 | width="1em" 96 | xmlns="http://www.w3.org/2000/svg" 97 | {...props} 98 | > 99 | <path fill="none" d="M0 0h24v24H0z"></path> 100 | <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z"></path> 101 | </svg> 102 | ); 103 | }; 104 | export const Company = (props) => { 105 | return ( 106 | <svg 107 | stroke="currentColor" 108 | fill="currentColor" 109 | strokeWidth="0" 110 | viewBox="0 0 24 24" 111 | height="1em" 112 | width="1em" 113 | xmlns="http://www.w3.org/2000/svg" 114 | {...props} 115 | > 116 | <path fill="none" d="M0 0h24v24H0z"></path> 117 | <path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"></path> 118 | </svg> 119 | ); 120 | }; 121 | export const LinkIcon = (props) => { 122 | return ( 123 | <svg 124 | stroke="currentColor" 125 | fill="currentColor" 126 | strokeWidth="0" 127 | viewBox="0 0 24 24" 128 | height="1em" 129 | width="1em" 130 | xmlns="http://www.w3.org/2000/svg" 131 | {...props} 132 | > 133 | <path fill="none" d="M0 0h24v24H0z"></path> 134 | <path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path> 135 | </svg> 136 | ); 137 | }; 138 | export const SearchIcon = (props) => { 139 | return ( 140 | <svg 141 | stroke="currentColor" 142 | fill="currentColor" 143 | strokeWidth="0" 144 | viewBox="0 0 24 24" 145 | height="1em" 146 | width="1em" 147 | xmlns="http://www.w3.org/2000/svg" 148 | {...props} 149 | > 150 | <path fill="none" d="M0 0h24v24H0z"></path> 151 | <path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path> 152 | </svg> 153 | ); 154 | }; 155 | 156 | 157 | export const StarIcon = (props) => { 158 | return ( 159 | <svg 160 | xmlns="http://www.w3.org/2000/svg" 161 | {...props} 162 | fill="none" 163 | viewBox="0 0 24 24" 164 | width="16px" 165 | height="16px" 166 | {...props} 167 | stroke="currentColor"> 168 | <path strokeLinecap="round" strokeLinejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" /> 169 | </svg> 170 | ); 171 | }; 172 | 173 | 174 | export const HamburgerIcon = (props) => { 175 | return ( 176 | <svg xmlns="http://www.w3.org/2000/svg" 177 | {...props} 178 | fill="none" 179 | viewBox="0 0 24 24" 180 | strokeWidth={1.5} 181 | stroke="currentColor" 182 | className="w-6 h-6"> 183 | <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /> 184 | </svg> 185 | ); 186 | }; 187 | 188 | 189 | export const NonFollowersIcon = (props) => { 190 | return ( 191 | <svg xmlns="http://www.w3.org/2000/svg" 192 | {...props} 193 | fill="none" 194 | viewBox="0 0 24 24" 195 | strokeWidth={1.5} 196 | stroke="currentColor" 197 | className="w-6 h-6"> 198 | <path strokeLinecap="round" strokeLinejoin="round" d="M7.5 15h2.25m8.024-9.75c.011.05.028.1.052.148.591 1.2.924 2.55.924 3.977a8.96 8.96 0 01-.999 4.125m.023-8.25c-.076-.365.183-.75.575-.75h.908c.889 0 1.713.518 1.972 1.368.339 1.11.521 2.287.521 3.507 0 1.553-.295 3.036-.831 4.398C20.613 14.547 19.833 15 19 15h-1.053c-.472 0-.745-.556-.5-.96a8.95 8.95 0 00.303-.54m.023-8.25H16.48a4.5 4.5 0 01-1.423-.23l-3.114-1.04a4.5 4.5 0 00-1.423-.23H6.504c-.618 0-1.217.247-1.605.729A11.95 11.95 0 002.25 12c0 .434.023.863.068 1.285C2.427 14.306 3.346 15 4.372 15h3.126c.618 0 .991.724.725 1.282A7.471 7.471 0 007.5 19.5a2.25 2.25 0 002.25 2.25.75.75 0 00.75-.75v-.633c0-.573.11-1.14.322-1.672.304-.76.93-1.33 1.653-1.715a9.04 9.04 0 002.86-2.4c.498-.634 1.226-1.08 2.032-1.08h.384" /> 199 | </svg> 200 | 201 | ); 202 | }; 203 | 204 | export const RateIcon = (props) => { 205 | return ( 206 | <svg xmlns="http://www.w3.org/2000/svg" 207 | {...props} 208 | fill="none" 209 | viewBox="0 0 24 24" 210 | strokeWidth={1.5} 211 | stroke="currentColor" 212 | className="w-6 h-6"> 213 | <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3v11.25A2.25 2.25 0 006 16.5h2.25M3.75 3h-1.5m1.5 0h16.5m0 0h1.5m-1.5 0v11.25A2.25 2.25 0 0118 16.5h-2.25m-7.5 0h7.5m-7.5 0l-1 3m8.5-3l1 3m0 0l.5 1.5m-.5-1.5h-9.5m0 0l-.5 1.5m.75-9l3-3 2.148 2.148A12.061 12.061 0 0116.5 7.605" /> 214 | </svg> 215 | ); 216 | }; 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /src/layout/footer/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from './styles.module.scss'; 3 | import Logo from "../header/logo"; 4 | import Link from "next/link"; 5 | import github from '@/assets/github.svg' 6 | 7 | export default function Footer() { 8 | 9 | console.log(github); 10 | 11 | return <section className={styles.section__footer}> 12 | <div className={styles.footer__logo}> 13 | <Logo /> 14 | <p>Github Insight provides users with a broader perspective by leveraging a limited service offered by the Github API.</p> 15 | </div> 16 | <div className={styles.section__right}> 17 | <div> 18 | <a href="https://github.com/yasinelbuz/github-info" target="_blank" className={styles.open__source}> 19 | <div>Open Source</div> 20 | <img src={github.src} alt="github" /> 21 | </a> 22 | <div> 23 | <Link href="/">Home</Link> 24 | <Link href="/dashboard">Dashboard</Link> 25 | <Link href="/repos">Repos</Link> 26 | <Link href="/non-followers">Non-Followers</Link> 27 | <Link href="/rate">Rate</Link> 28 | </div> 29 | </div> 30 | </div> 31 | </section>; 32 | } 33 | -------------------------------------------------------------------------------- /src/layout/footer/styles.module.scss: -------------------------------------------------------------------------------- 1 | .section__footer { 2 | background-color: #000; 3 | padding-top: 40px; 4 | padding-bottom: 40px; 5 | display: flex; 6 | justify-content: space-between; 7 | align-items: flex-start; 8 | 9 | .footer__logo { 10 | width: 300px; 11 | 12 | p { 13 | color: hsla(0, 0%, 100%, 0.5); 14 | line-height: 140%; 15 | } 16 | } 17 | 18 | .section__right { 19 | .open__source { 20 | display: flex; 21 | align-items: center; 22 | gap: 10px; 23 | } 24 | 25 | a { 26 | display: flex; 27 | flex-direction: column; 28 | color: #fff; 29 | 30 | img { 31 | width: 24px; 32 | } 33 | } 34 | 35 | & > div { 36 | display: flex; 37 | align-items: center; 38 | gap: 20px; 39 | } 40 | } 41 | } 42 | 43 | @media only screen and (max-width: 700px) { 44 | .section__footer { 45 | flex-direction: column; 46 | gap: 40px; 47 | align-items: center; 48 | justify-content: center; 49 | text-align: center; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/layout/header/hamburgerBox.js: -------------------------------------------------------------------------------- 1 | //icons 2 | import { HamburgerIcon } from "@/icons"; 3 | 4 | //styles 5 | import styles from './styles.module.scss'; 6 | 7 | //redux 8 | import { useDispatch, useSelector } from "react-redux"; 9 | import { setOpenMobileMenu } from "@/redux/theme"; 10 | 11 | export default function HamburgerBox() { 12 | 13 | const dispatch = useDispatch(); 14 | const { isOpenMobileMenu } = useSelector((state) => state.theme) 15 | 16 | const handleOpen = () => { 17 | dispatch(setOpenMobileMenu(!isOpenMobileMenu)) 18 | } 19 | 20 | return <div className={styles.hamburger__icon} onClick={handleOpen}> 21 | <HamburgerIcon /> 22 | </div>; 23 | } 24 | -------------------------------------------------------------------------------- /src/layout/header/index.js: -------------------------------------------------------------------------------- 1 | //styles 2 | import styles from './styles.module.scss'; 3 | 4 | //component 5 | import SidebarLeft from "./sidebarLeft"; 6 | import SidebarRight from "./sidebarRight"; 7 | import MobileMenu from "./mobileMenu"; 8 | 9 | export default function Header() { 10 | 11 | return <> 12 | <header className={styles.header}> 13 | <section> 14 | <SidebarLeft /> 15 | <SidebarRight /> 16 | </section> 17 | </header> 18 | <MobileMenu /> 19 | </> 20 | } 21 | -------------------------------------------------------------------------------- /src/layout/header/logo.js: -------------------------------------------------------------------------------- 1 | //image 2 | import logo from '@/assets/GitHubInsight__Logo.png'; 3 | 4 | //next 5 | import Image from 'next/image'; 6 | import Link from 'next/link'; 7 | 8 | //styles 9 | import styles from './styles.module.scss'; 10 | 11 | export default function Logo() { 12 | return <div className={styles.logo}> 13 | <Link href="/"> 14 | <Image 15 | src={logo} 16 | alt="Github Insight Logo" 17 | /> 18 | </Link> 19 | </div>; 20 | } 21 | -------------------------------------------------------------------------------- /src/layout/header/mobileMenu.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | //styles 4 | import styles from './styles.module.scss'; 5 | 6 | //components 7 | import Search from "@/components/search"; 8 | import Navigation from "./navigation"; 9 | 10 | //redux 11 | import { useSelector } from "react-redux"; 12 | 13 | export default function MobileMenu() { 14 | 15 | const { isOpenMobileMenu } = useSelector((state) => state.theme); 16 | 17 | return ( 18 | <> 19 | {isOpenMobileMenu && <div className={styles.mobile__menu}> 20 | <section> 21 | <div className={styles.search__box}> 22 | <Search style={{ width: "100%" }} /> 23 | </div> 24 | <Navigation /> 25 | </section> 26 | </div>} 27 | 28 | </> 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/layout/header/navigation.js: -------------------------------------------------------------------------------- 1 | //styles 2 | import styles from './styles.module.scss'; 3 | 4 | //next 5 | import Link from "next/link"; 6 | 7 | export default function Navigation() { 8 | return <nav className={styles.navigation}> 9 | <Link href="/">Home</Link> 10 | <Link href="/dashboard">Dashboard</Link> 11 | <Link href="/repos">Repos</Link> 12 | <Link href="/non-followers">Non-Followers</Link> 13 | <Link href="/rate">Rate</Link> 14 | </nav>; 15 | } 16 | -------------------------------------------------------------------------------- /src/layout/header/sidebarLeft.js: -------------------------------------------------------------------------------- 1 | 2 | //components 3 | import Search from '@/components/search'; 4 | import HamburgerBox from './hamburgerBox'; 5 | import Logo from './logo'; 6 | import Navigation from './navigation'; 7 | 8 | //styles 9 | import styles from './styles.module.scss'; 10 | 11 | export default function SidebarLeft() { 12 | return <> 13 | {/* Sidebar Left */} 14 | <div className={styles.sidebar__left}> 15 | <HamburgerBox /> 16 | <Logo /> 17 | <div className={styles.serach__box}> 18 | <Search /> 19 | </div> 20 | <Navigation /> 21 | </div> 22 | </>; 23 | } 24 | -------------------------------------------------------------------------------- /src/layout/header/sidebarRight.js: -------------------------------------------------------------------------------- 1 | //styles 2 | import styles from './styles.module.scss'; 3 | 4 | //redux 5 | import { useSelector } from 'react-redux'; 6 | 7 | //services 8 | import { useGetGithubUserByNameQuery } from '@/services/users'; 9 | 10 | export default function SidebarRight() { 11 | 12 | const { search } = useSelector(state => state.users); 13 | const { data } = useGetGithubUserByNameQuery(search) 14 | 15 | return <> 16 | {/* Sidebar Right */} 17 | <div className={styles.sidebar__right}> 18 | <span>{data?.login}</span> 19 | <img 20 | src={data?.avatar_url} 21 | width="50px" 22 | height="50px" 23 | alt="profil" 24 | /> 25 | </div> 26 | </>; 27 | } 28 | -------------------------------------------------------------------------------- /src/layout/header/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/_variables"; 2 | 3 | .header { 4 | background-color: #1f262f; 5 | position: sticky; 6 | top: 0; 7 | z-index: 9999; 8 | box-shadow: 0px 1px 20px rgba(0, 0, 0, 0.1); 9 | 10 | section { 11 | height: 60px; 12 | display: flex; 13 | align-items: center; 14 | justify-content: space-between; 15 | } 16 | 17 | .sidebar__left { 18 | display: flex; 19 | align-items: center; 20 | 21 | .logo { 22 | cursor: pointer; 23 | } 24 | 25 | nav.navigation { 26 | display: flex; 27 | gap: 1.5rem; 28 | a { 29 | font-weight: 600; 30 | font-size: 14px; 31 | line-height: 17px; 32 | color: #ffffff; 33 | } 34 | } 35 | 36 | .hamburger__icon { 37 | display: none; 38 | width: 24px; 39 | cursor: pointer; 40 | margin-right: 5px; 41 | 42 | svg { 43 | color: #fff; 44 | } 45 | } 46 | } 47 | 48 | .sidebar__right { 49 | display: flex; 50 | align-items: center; 51 | gap: 10px; 52 | color: #fff; 53 | font-size: 1.5rem; 54 | font-weight: bold; 55 | img { 56 | width: 40px; 57 | height: 40px; 58 | border-radius: 50%; 59 | } 60 | } 61 | } 62 | 63 | .mobile__menu { 64 | width: 100%; 65 | background-color: $bg-color_2; 66 | margin-bottom: 20px; 67 | } 68 | 69 | @media only screen and (min-width: 951px) { 70 | .mobile__menu { 71 | display: none; 72 | } 73 | } 74 | 75 | @media only screen and (max-width: 950px) { 76 | .header .sidebar__left nav.navigation { 77 | display: none; 78 | } 79 | 80 | .header .sidebar__left .serach__box { 81 | display: none; 82 | } 83 | 84 | .header .sidebar__left .hamburger__icon { 85 | display: block; 86 | } 87 | 88 | .mobile__menu { 89 | padding-top: 20px; 90 | padding-bottom: 20px; 91 | } 92 | 93 | .mobile__menu .navigation { 94 | display: flex; 95 | flex-direction: column; 96 | justify-items: center; 97 | 98 | a { 99 | background-color: #1f262f; 100 | margin-top: 10px; 101 | padding: 10px; 102 | border-radius: 20px; 103 | text-align: center; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/layout/info/index.js: -------------------------------------------------------------------------------- 1 | //core 2 | import React, { useEffect } from "react"; 3 | 4 | //styles 5 | import styles from './styles.module.scss' 6 | 7 | //services 8 | import { useGetRateLimitQuery } from "@/services/rate"; 9 | 10 | //icons 11 | import { StarIcon } from "@/icons"; 12 | 13 | export default function Info() { 14 | 15 | const { data, refetch } = useGetRateLimitQuery(); 16 | 17 | useEffect(() => { 18 | refetch(); 19 | }) 20 | 21 | let remaining = data?.rate?.remaining; 22 | let limit = data?.rate?.limit; 23 | 24 | return (<> 25 | <header className={styles.header}> 26 | <section> 27 | <div className={styles.rate}> 28 | My remaining request count is <b>{remaining}</b> out of <b>{limit}</b> 29 | </div> 30 | <div> 31 | <a href={process.env.NEXT_PUBLIC_PROJECT_GITHUB_URL} target="_blank" className={styles.star_github_btn}><StarIcon /> Star on Github</a> 32 | </div> 33 | </section> 34 | </header> 35 | </>); 36 | } 37 | -------------------------------------------------------------------------------- /src/layout/info/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/_variables"; 2 | 3 | .header { 4 | background-color: #ea5455; 5 | color: #fff; 6 | font-size: 1.5rem; 7 | 8 | section { 9 | height: 50px; 10 | display: flex; 11 | justify-content: space-between; 12 | align-items: center; 13 | } 14 | 15 | .rate { 16 | font-weight: normal; 17 | opacity: 0.5; 18 | } 19 | 20 | .star_github_btn { 21 | background-color: #efefef; 22 | padding: 5px 10px; 23 | border-radius: 10px; 24 | display: flex; 25 | align-items: center; 26 | gap: 0.5rem; 27 | transition: all 0.1s; 28 | 29 | &:hover { 30 | transform: scale(1.1); 31 | svg { 32 | fill: $primary-color; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | //styles 2 | import '@/styles/globals.scss' 3 | 4 | //components 5 | import Header from '@/layout/header' 6 | import Info from '@/layout/info'; 7 | import Footer from '@/layout/footer'; 8 | 9 | //redux 10 | import { Provider } from 'react-redux' 11 | import { store } from '@/redux/store' 12 | 13 | //next 14 | import Head from 'next/head'; 15 | 16 | export default function App({ Component, pageProps }) { 17 | return ( 18 | <> 19 | <Head> 20 | <title>Github Insight 21 | 22 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |